fix # 7511-New_Customer_Portal_201e_Completion
[roojs1] / roojs-bootstrap-debug.js
1 Roo.bootstrap = {};/**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
21  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
240  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
241  
242  * @constructor
243  * Do not use directly - it does not do anything..
244  * @param {Object} config The config object
245  */
246
247
248
249 Roo.bootstrap.Component = function(config){
250     Roo.bootstrap.Component.superclass.constructor.call(this, config);
251        
252     this.addEvents({
253         /**
254          * @event childrenrendered
255          * Fires when the children have been rendered..
256          * @param {Roo.bootstrap.Component} this
257          */
258         "childrenrendered" : true
259         
260         
261         
262     });
263     
264     
265 };
266
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
268     
269     
270     allowDomMove : false, // to stop relocations in parent onRender...
271     
272     cls : false,
273     
274     style : false,
275     
276     autoCreate : false,
277     
278     tooltip : null,
279     /**
280      * Initialize Events for the element
281      */
282     initEvents : function() { },
283     
284     xattr : false,
285     
286     parentId : false,
287     
288     can_build_overlaid : true,
289     
290     container_method : false,
291     
292     dataId : false,
293     
294     name : false,
295     
296     parent: function() {
297         // returns the parent component..
298         return Roo.ComponentMgr.get(this.parentId)
299         
300         
301     },
302     
303     // private
304     onRender : function(ct, position)
305     {
306        // Roo.log("Call onRender: " + this.xtype);
307         
308         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
309         
310         if(this.el){
311             if (this.el.attr('xtype')) {
312                 this.el.attr('xtypex', this.el.attr('xtype'));
313                 this.el.dom.removeAttribute('xtype');
314                 
315                 this.initEvents();
316             }
317             
318             return;
319         }
320         
321          
322         
323         var cfg = Roo.apply({},  this.getAutoCreate());
324         
325         cfg.id = this.id || Roo.id();
326         
327         // fill in the extra attributes 
328         if (this.xattr && typeof(this.xattr) =='object') {
329             for (var i in this.xattr) {
330                 cfg[i] = this.xattr[i];
331             }
332         }
333         
334         if(this.dataId){
335             cfg.dataId = this.dataId;
336         }
337         
338         if (this.cls) {
339             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
340         }
341         
342         if (this.style) { // fixme needs to support more complex style data.
343             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344         }
345         
346         if(this.name){
347             cfg.name = this.name;
348         }
349         
350         this.el = ct.createChild(cfg, position);
351         
352         if (this.tooltip) {
353             this.tooltipEl().attr('tooltip', this.tooltip);
354         }
355         
356         if(this.tabIndex !== undefined){
357             this.el.dom.setAttribute('tabIndex', this.tabIndex);
358         }
359         
360         this.initEvents();
361         
362     },
363     /**
364      * Fetch the element to add children to
365      * @return {Roo.Element} defaults to this.el
366      */
367     getChildContainer : function()
368     {
369         return this.el;
370     },
371     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
372     {
373         return Roo.get(document.body);
374     },
375     
376     /**
377      * Fetch the element to display the tooltip on.
378      * @return {Roo.Element} defaults to this.el
379      */
380     tooltipEl : function()
381     {
382         return this.el;
383     },
384         
385     addxtype  : function(tree,cntr)
386     {
387         var cn = this;
388         
389         cn = Roo.factory(tree);
390         //Roo.log(['addxtype', cn]);
391            
392         cn.parentType = this.xtype; //??
393         cn.parentId = this.id;
394         
395         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396         if (typeof(cn.container_method) == 'string') {
397             cntr = cn.container_method;
398         }
399         
400         
401         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
402         
403         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
404         
405         var build_from_html =  Roo.XComponent.build_from_html;
406           
407         var is_body  = (tree.xtype == 'Body') ;
408           
409         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
410           
411         var self_cntr_el = Roo.get(this[cntr](false));
412         
413         // do not try and build conditional elements 
414         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415             return false;
416         }
417         
418         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420                 return this.addxtypeChild(tree,cntr, is_body);
421             }
422             
423             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
424                 
425             if(echild){
426                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
427             }
428             
429             Roo.log('skipping render');
430             return cn;
431             
432         }
433         
434         var ret = false;
435         if (!build_from_html) {
436             return false;
437         }
438         
439         // this i think handles overlaying multiple children of the same type
440         // with the sam eelement.. - which might be buggy..
441         while (true) {
442             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
443             
444             if (!echild) {
445                 break;
446             }
447             
448             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449                 break;
450             }
451             
452             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453         }
454        
455         return ret;
456     },
457     
458     
459     addxtypeChild : function (tree, cntr, is_body)
460     {
461         Roo.debug && Roo.log('addxtypeChild:' + cntr);
462         var cn = this;
463         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
464         
465         
466         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467                     (typeof(tree['flexy:foreach']) != 'undefined');
468           
469     
470         
471         skip_children = false;
472         // render the element if it's not BODY.
473         if (!is_body) {
474             
475             // if parent was disabled, then do not try and create the children..
476             if(!this[cntr](true)){
477                 tree.items = [];
478                 return tree;
479             }
480            
481             cn = Roo.factory(tree);
482            
483             cn.parentType = this.xtype; //??
484             cn.parentId = this.id;
485             
486             var build_from_html =  Roo.XComponent.build_from_html;
487             
488             
489             // does the container contain child eleemnts with 'xtype' attributes.
490             // that match this xtype..
491             // note - when we render we create these as well..
492             // so we should check to see if body has xtype set.
493             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
494                
495                 var self_cntr_el = Roo.get(this[cntr](false));
496                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
497                 if (echild) { 
498                     //Roo.log(Roo.XComponent.build_from_html);
499                     //Roo.log("got echild:");
500                     //Roo.log(echild);
501                 }
502                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503                 // and are not displayed -this causes this to use up the wrong element when matching.
504                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
505                 
506                 
507                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
509                   
510                   
511                   
512                     cn.el = echild;
513                   //  Roo.log("GOT");
514                     //echild.dom.removeAttribute('xtype');
515                 } else {
516                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517                     Roo.debug && Roo.log(self_cntr_el);
518                     Roo.debug && Roo.log(echild);
519                     Roo.debug && Roo.log(cn);
520                 }
521             }
522            
523             
524            
525             // if object has flexy:if - then it may or may not be rendered.
526             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
527                 // skip a flexy if element.
528                 Roo.debug && Roo.log('skipping render');
529                 Roo.debug && Roo.log(tree);
530                 if (!cn.el) {
531                     Roo.debug && Roo.log('skipping all children');
532                     skip_children = true;
533                 }
534                 
535              } else {
536                  
537                 // actually if flexy:foreach is found, we really want to create 
538                 // multiple copies here...
539                 //Roo.log('render');
540                 //Roo.log(this[cntr]());
541                 // some elements do not have render methods.. like the layouts...
542                 /*
543                 if(this[cntr](true) === false){
544                     cn.items = [];
545                     return cn;
546                 }
547                 */
548                 cn.render && cn.render(this[cntr](true));
549                 
550              }
551             // then add the element..
552         }
553          
554         // handle the kids..
555         
556         var nitems = [];
557         /*
558         if (typeof (tree.menu) != 'undefined') {
559             tree.menu.parentType = cn.xtype;
560             tree.menu.triggerEl = cn.el;
561             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562             
563         }
564         */
565         if (!tree.items || !tree.items.length) {
566             cn.items = nitems;
567             //Roo.log(["no children", this]);
568             
569             return cn;
570         }
571          
572         var items = tree.items;
573         delete tree.items;
574         
575         //Roo.log(items.length);
576             // add the items..
577         if (!skip_children) {    
578             for(var i =0;i < items.length;i++) {
579               //  Roo.log(['add child', items[i]]);
580                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
581             }
582         }
583         
584         cn.items = nitems;
585         
586         //Roo.log("fire childrenrendered");
587         
588         cn.fireEvent('childrenrendered', this);
589         
590         return cn;
591     },
592     
593     /**
594      * Set the element that will be used to show or hide
595      */
596     setVisibilityEl : function(el)
597     {
598         this.visibilityEl = el;
599     },
600     
601      /**
602      * Get the element that will be used to show or hide
603      */
604     getVisibilityEl : function()
605     {
606         if (typeof(this.visibilityEl) == 'object') {
607             return this.visibilityEl;
608         }
609         
610         if (typeof(this.visibilityEl) == 'string') {
611             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612         }
613         
614         return this.getEl();
615     },
616     
617     /**
618      * Show a component - removes 'hidden' class
619      */
620     show : function()
621     {
622         if(!this.getVisibilityEl()){
623             return;
624         }
625          
626         this.getVisibilityEl().removeClass(['hidden','d-none']);
627         
628         this.fireEvent('show', this);
629         
630         
631     },
632     /**
633      * Hide a component - adds 'hidden' class
634      */
635     hide: function()
636     {
637         if(!this.getVisibilityEl()){
638             return;
639         }
640         
641         this.getVisibilityEl().addClass(['hidden','d-none']);
642         
643         this.fireEvent('hide', this);
644         
645     }
646 });
647
648  /*
649  * - LGPL
650  *
651  * element
652  * 
653  */
654
655 /**
656  * @class Roo.bootstrap.Element
657  * @extends Roo.bootstrap.Component
658  * @children Roo.bootstrap.Component
659  * Bootstrap Element class (basically a DIV used to make random stuff )
660  * 
661  * @cfg {String} html contents of the element
662  * @cfg {String} tag tag of the element
663  * @cfg {String} cls class of the element
664  * @cfg {Boolean} preventDefault (true|false) default false
665  * @cfg {Boolean} clickable (true|false) default false
666  * @cfg {String} role default blank - set to button to force cursor pointer
667  
668  * 
669  * @constructor
670  * Create a new Element
671  * @param {Object} config The config object
672  */
673
674 Roo.bootstrap.Element = function(config){
675     Roo.bootstrap.Element.superclass.constructor.call(this, config);
676     
677     this.addEvents({
678         // raw events
679         /**
680          * @event click
681          * When a element is chick
682          * @param {Roo.bootstrap.Element} this
683          * @param {Roo.EventObject} e
684          */
685         "click" : true 
686         
687       
688     });
689 };
690
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
692     
693     tag: 'div',
694     cls: '',
695     html: '',
696     preventDefault: false, 
697     clickable: false,
698     tapedTwice : false,
699     role : false,
700     
701     getAutoCreate : function(){
702         
703         var cfg = {
704             tag: this.tag,
705             // cls: this.cls, double assign in parent class Component.js :: onRender
706             html: this.html
707         };
708         if (this.role !== false) {
709             cfg.role = this.role;
710         }
711         
712         return cfg;
713     },
714     
715     initEvents: function() 
716     {
717         Roo.bootstrap.Element.superclass.initEvents.call(this);
718         
719         if(this.clickable){
720             this.el.on('click', this.onClick, this);
721         }
722         
723         
724     },
725     
726     onClick : function(e)
727     {
728         if(this.preventDefault){
729             e.preventDefault();
730         }
731         
732         this.fireEvent('click', this, e); // why was this double click before?
733     },
734     
735     
736     
737
738     
739     
740     getValue : function()
741     {
742         return this.el.dom.innerHTML;
743     },
744     
745     setValue : function(value)
746     {
747         this.el.dom.innerHTML = value;
748     }
749    
750 });
751
752  
753
754  /*
755  * - LGPL
756  *
757  * dropable area
758  * 
759  */
760
761 /**
762  * @class Roo.bootstrap.DropTarget
763  * @extends Roo.bootstrap.Element
764  * Bootstrap DropTarget class
765  
766  * @cfg {string} name dropable name
767  * 
768  * @constructor
769  * Create a new Dropable Area
770  * @param {Object} config The config object
771  */
772
773 Roo.bootstrap.DropTarget = function(config){
774     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
775     
776     this.addEvents({
777         // raw events
778         /**
779          * @event click
780          * When a element is chick
781          * @param {Roo.bootstrap.Element} this
782          * @param {Roo.EventObject} e
783          */
784         "drop" : true
785     });
786 };
787
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
789     
790     
791     getAutoCreate : function(){
792         
793          
794     },
795     
796     initEvents: function() 
797     {
798         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
800             ddGroup: this.name,
801             listeners : {
802                 drop : this.dragDrop.createDelegate(this),
803                 enter : this.dragEnter.createDelegate(this),
804                 out : this.dragOut.createDelegate(this),
805                 over : this.dragOver.createDelegate(this)
806             }
807             
808         });
809         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
810     },
811     
812     dragDrop : function(source,e,data)
813     {
814         // user has to decide how to impliment this.
815         Roo.log('drop');
816         Roo.log(this);
817         //this.fireEvent('drop', this, source, e ,data);
818         return false;
819     },
820     
821     dragEnter : function(n, dd, e, data)
822     {
823         // probably want to resize the element to match the dropped element..
824         Roo.log("enter");
825         this.originalSize = this.el.getSize();
826         this.el.setSize( n.el.getSize());
827         this.dropZone.DDM.refreshCache(this.name);
828         Roo.log([n, dd, e, data]);
829     },
830     
831     dragOut : function(value)
832     {
833         // resize back to normal
834         Roo.log("out");
835         this.el.setSize(this.originalSize);
836         this.dropZone.resetConstraints();
837     },
838     
839     dragOver : function()
840     {
841         // ??? do nothing?
842     }
843    
844 });
845
846  
847
848  /*
849  * - LGPL
850  *
851  * Body
852  *
853  */
854
855 /**
856  * @class Roo.bootstrap.Body
857  * @extends Roo.bootstrap.Component
858  * @children Roo.bootstrap.Component 
859  * @parent none builder
860  * Bootstrap Body class
861  *
862  * @constructor
863  * Create a new body
864  * @param {Object} config The config object
865  */
866
867 Roo.bootstrap.Body = function(config){
868
869     config = config || {};
870
871     Roo.bootstrap.Body.superclass.constructor.call(this, config);
872     this.el = Roo.get(config.el ? config.el : document.body );
873     if (this.cls && this.cls.length) {
874         Roo.get(document.body).addClass(this.cls);
875     }
876 };
877
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
879
880     is_body : true,// just to make sure it's constructed?
881
882         autoCreate : {
883         cls: 'container'
884     },
885     onRender : function(ct, position)
886     {
887        /* Roo.log("Roo.bootstrap.Body - onRender");
888         if (this.cls && this.cls.length) {
889             Roo.get(document.body).addClass(this.cls);
890         }
891         // style??? xttr???
892         */
893     }
894
895
896
897
898 });
899 /*
900  * - LGPL
901  *
902  * button group
903  * 
904  */
905
906
907 /**
908  * @class Roo.bootstrap.ButtonGroup
909  * @extends Roo.bootstrap.Component
910  * Bootstrap ButtonGroup class
911  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
912  * 
913  * @cfg {String} size lg | sm | xs (default empty normal)
914  * @cfg {String} align vertical | justified  (default none)
915  * @cfg {String} direction up | down (default down)
916  * @cfg {Boolean} toolbar false | true
917  * @cfg {Boolean} btn true | false
918  * 
919  * 
920  * @constructor
921  * Create a new Input
922  * @param {Object} config The config object
923  */
924
925 Roo.bootstrap.ButtonGroup = function(config){
926     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
927 };
928
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
930     
931     size: '',
932     align: '',
933     direction: '',
934     toolbar: false,
935     btn: true,
936
937     getAutoCreate : function(){
938         var cfg = {
939             cls: 'btn-group',
940             html : null
941         };
942         
943         cfg.html = this.html || cfg.html;
944         
945         if (this.toolbar) {
946             cfg = {
947                 cls: 'btn-toolbar',
948                 html: null
949             };
950             
951             return cfg;
952         }
953         
954         if (['vertical','justified'].indexOf(this.align)!==-1) {
955             cfg.cls = 'btn-group-' + this.align;
956             
957             if (this.align == 'justified') {
958                 console.log(this.items);
959             }
960         }
961         
962         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963             cfg.cls += ' btn-group-' + this.size;
964         }
965         
966         if (this.direction == 'up') {
967             cfg.cls += ' dropup' ;
968         }
969         
970         return cfg;
971     },
972     /**
973      * Add a button to the group (similar to NavItem API.)
974      */
975     addItem : function(cfg)
976     {
977         var cn = new Roo.bootstrap.Button(cfg);
978         //this.register(cn);
979         cn.parentId = this.id;
980         cn.onRender(this.el, null);
981         return cn;
982     }
983    
984 });
985
986  /*
987  * - LGPL
988  *
989  * button
990  * 
991  */
992
993 /**
994  * @class Roo.bootstrap.Button
995  * @extends Roo.bootstrap.Component
996  * Bootstrap Button class
997  * @cfg {String} html The button content
998  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001  * @cfg {String} size (lg|sm|xs)
1002  * @cfg {String} tag (a|input|submit)
1003  * @cfg {String} href empty or href
1004  * @cfg {Boolean} disabled default false;
1005  * @cfg {Boolean} isClose default false;
1006  * @cfg {String} glyphicon depricated - use fa
1007  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008  * @cfg {String} badge text for badge
1009  * @cfg {String} theme (default|glow)  
1010  * @cfg {Boolean} inverse dark themed version
1011  * @cfg {Boolean} toggle is it a slidy toggle button
1012  * @cfg {Boolean} pressed   default null - if the button ahs active state
1013  * @cfg {String} ontext text for on slidy toggle state
1014  * @cfg {String} offtext text for off slidy toggle state
1015  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1016  * @cfg {Boolean} removeClass remove the standard class..
1017  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1018  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1020
1021  * @constructor
1022  * Create a new button
1023  * @param {Object} config The config object
1024  */
1025
1026
1027 Roo.bootstrap.Button = function(config){
1028     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1029     
1030     this.addEvents({
1031         // raw events
1032         /**
1033          * @event click
1034          * When a button is pressed
1035          * @param {Roo.bootstrap.Button} btn
1036          * @param {Roo.EventObject} e
1037          */
1038         "click" : true,
1039         /**
1040          * @event dblclick
1041          * When a button is double clicked
1042          * @param {Roo.bootstrap.Button} btn
1043          * @param {Roo.EventObject} e
1044          */
1045         "dblclick" : true,
1046          /**
1047          * @event toggle
1048          * After the button has been toggles
1049          * @param {Roo.bootstrap.Button} btn
1050          * @param {Roo.EventObject} e
1051          * @param {boolean} pressed (also available as button.pressed)
1052          */
1053         "toggle" : true
1054     });
1055 };
1056
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1058     html: false,
1059     active: false,
1060     weight: '',
1061     badge_weight: '',
1062     outline : false,
1063     size: '',
1064     tag: 'button',
1065     href: '',
1066     disabled: false,
1067     isClose: false,
1068     glyphicon: '',
1069     fa: '',
1070     badge: '',
1071     theme: 'default',
1072     inverse: false,
1073     
1074     toggle: false,
1075     ontext: 'ON',
1076     offtext: 'OFF',
1077     defaulton: true,
1078     preventDefault: true,
1079     removeClass: false,
1080     name: false,
1081     target: false,
1082     group : false,
1083      
1084     pressed : null,
1085      
1086     
1087     getAutoCreate : function(){
1088         
1089         var cfg = {
1090             tag : 'button',
1091             cls : 'roo-button',
1092             html: ''
1093         };
1094         
1095         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097             this.tag = 'button';
1098         } else {
1099             cfg.tag = this.tag;
1100         }
1101         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1102         
1103         if (this.toggle == true) {
1104             cfg={
1105                 tag: 'div',
1106                 cls: 'slider-frame roo-button',
1107                 cn: [
1108                     {
1109                         tag: 'span',
1110                         'data-on-text':'ON',
1111                         'data-off-text':'OFF',
1112                         cls: 'slider-button',
1113                         html: this.offtext
1114                     }
1115                 ]
1116             };
1117             // why are we validating the weights?
1118             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119                 cfg.cls +=  ' ' + this.weight;
1120             }
1121             
1122             return cfg;
1123         }
1124         
1125         if (this.isClose) {
1126             cfg.cls += ' close';
1127             
1128             cfg["aria-hidden"] = true;
1129             
1130             cfg.html = "&times;";
1131             
1132             return cfg;
1133         }
1134              
1135         
1136         if (this.theme==='default') {
1137             cfg.cls = 'btn roo-button';
1138             
1139             //if (this.parentType != 'Navbar') {
1140             this.weight = this.weight.length ?  this.weight : 'default';
1141             //}
1142             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1143                 
1144                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146                 cfg.cls += ' btn-' + outline + weight;
1147                 if (this.weight == 'default') {
1148                     // BC
1149                     cfg.cls += ' btn-' + this.weight;
1150                 }
1151             }
1152         } else if (this.theme==='glow') {
1153             
1154             cfg.tag = 'a';
1155             cfg.cls = 'btn-glow roo-button';
1156             
1157             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1158                 
1159                 cfg.cls += ' ' + this.weight;
1160             }
1161         }
1162    
1163         
1164         if (this.inverse) {
1165             this.cls += ' inverse';
1166         }
1167         
1168         
1169         if (this.active || this.pressed === true) {
1170             cfg.cls += ' active';
1171         }
1172         
1173         if (this.disabled) {
1174             cfg.disabled = 'disabled';
1175         }
1176         
1177         if (this.items) {
1178             Roo.log('changing to ul' );
1179             cfg.tag = 'ul';
1180             this.glyphicon = 'caret';
1181             if (Roo.bootstrap.version == 4) {
1182                 this.fa = 'caret-down';
1183             }
1184             
1185         }
1186         
1187         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1188          
1189         //gsRoo.log(this.parentType);
1190         if (this.parentType === 'Navbar' && !this.parent().bar) {
1191             Roo.log('changing to li?');
1192             
1193             cfg.tag = 'li';
1194             
1195             cfg.cls = '';
1196             cfg.cn =  [{
1197                 tag : 'a',
1198                 cls : 'roo-button',
1199                 html : this.html,
1200                 href : this.href || '#'
1201             }];
1202             if (this.menu) {
1203                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1204                 cfg.cls += ' dropdown';
1205             }   
1206             
1207             delete cfg.html;
1208             
1209         }
1210         
1211        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1212         
1213         if (this.glyphicon) {
1214             cfg.html = ' ' + cfg.html;
1215             
1216             cfg.cn = [
1217                 {
1218                     tag: 'span',
1219                     cls: 'glyphicon glyphicon-' + this.glyphicon
1220                 }
1221             ];
1222         }
1223         if (this.fa) {
1224             cfg.html = ' ' + cfg.html;
1225             
1226             cfg.cn = [
1227                 {
1228                     tag: 'i',
1229                     cls: 'fa fas fa-' + this.fa
1230                 }
1231             ];
1232         }
1233         
1234         if (this.badge) {
1235             cfg.html += ' ';
1236             
1237             cfg.tag = 'a';
1238             
1239 //            cfg.cls='btn roo-button';
1240             
1241             cfg.href=this.href;
1242             
1243             var value = cfg.html;
1244             
1245             if(this.glyphicon){
1246                 value = {
1247                     tag: 'span',
1248                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1249                     html: this.html
1250                 };
1251             }
1252             if(this.fa){
1253                 value = {
1254                     tag: 'i',
1255                     cls: 'fa fas fa-' + this.fa,
1256                     html: this.html
1257                 };
1258             }
1259             
1260             var bw = this.badge_weight.length ? this.badge_weight :
1261                 (this.weight.length ? this.weight : 'secondary');
1262             bw = bw == 'default' ? 'secondary' : bw;
1263             
1264             cfg.cn = [
1265                 value,
1266                 {
1267                     tag: 'span',
1268                     cls: 'badge badge-' + bw,
1269                     html: this.badge
1270                 }
1271             ];
1272             
1273             cfg.html='';
1274         }
1275         
1276         if (this.menu) {
1277             cfg.cls += ' dropdown';
1278             cfg.html = typeof(cfg.html) != 'undefined' ?
1279                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1280         }
1281         
1282         if (cfg.tag !== 'a' && this.href !== '') {
1283             throw "Tag must be a to set href.";
1284         } else if (this.href.length > 0) {
1285             cfg.href = this.href;
1286         }
1287         
1288         if(this.removeClass){
1289             cfg.cls = '';
1290         }
1291         
1292         if(this.target){
1293             cfg.target = this.target;
1294         }
1295         
1296         return cfg;
1297     },
1298     initEvents: function() {
1299        // Roo.log('init events?');
1300 //        Roo.log(this.el.dom);
1301         // add the menu...
1302         
1303         if (typeof (this.menu) != 'undefined') {
1304             this.menu.parentType = this.xtype;
1305             this.menu.triggerEl = this.el;
1306             this.addxtype(Roo.apply({}, this.menu));
1307         }
1308
1309
1310         if (this.el.hasClass('roo-button')) {
1311              this.el.on('click', this.onClick, this);
1312              this.el.on('dblclick', this.onDblClick, this);
1313         } else {
1314              this.el.select('.roo-button').on('click', this.onClick, this);
1315              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1316              
1317         }
1318         // why?
1319         if(this.removeClass){
1320             this.el.on('click', this.onClick, this);
1321         }
1322         
1323         if (this.group === true) {
1324              if (this.pressed === false || this.pressed === true) {
1325                 // nothing
1326             } else {
1327                 this.pressed = false;
1328                 this.setActive(this.pressed);
1329             }
1330             
1331         }
1332         
1333         this.el.enableDisplayMode();
1334         
1335     },
1336     onClick : function(e)
1337     {
1338         if (this.disabled) {
1339             return;
1340         }
1341         
1342         Roo.log('button on click ');
1343         if(this.href === '' || this.preventDefault){
1344             e.preventDefault();
1345         }
1346         
1347         if (this.group) {
1348             if (this.pressed) {
1349                 // do nothing -
1350                 return;
1351             }
1352             this.setActive(true);
1353             var pi = this.parent().items;
1354             for (var i = 0;i < pi.length;i++) {
1355                 if (this == pi[i]) {
1356                     continue;
1357                 }
1358                 if (pi[i].el.hasClass('roo-button')) {
1359                     pi[i].setActive(false);
1360                 }
1361             }
1362             this.fireEvent('click', this, e);            
1363             return;
1364         }
1365         
1366         if (this.pressed === true || this.pressed === false) {
1367             this.toggleActive(e);
1368         }
1369         
1370         
1371         this.fireEvent('click', this, e);
1372     },
1373     onDblClick: function(e)
1374     {
1375         if (this.disabled) {
1376             return;
1377         }
1378         if(this.preventDefault){
1379             e.preventDefault();
1380         }
1381         this.fireEvent('dblclick', this, e);
1382     },
1383     /**
1384      * Enables this button
1385      */
1386     enable : function()
1387     {
1388         this.disabled = false;
1389         this.el.removeClass('disabled');
1390         this.el.dom.removeAttribute("disabled");
1391     },
1392     
1393     /**
1394      * Disable this button
1395      */
1396     disable : function()
1397     {
1398         this.disabled = true;
1399         this.el.addClass('disabled');
1400         this.el.attr("disabled", "disabled")
1401     },
1402      /**
1403      * sets the active state on/off, 
1404      * @param {Boolean} state (optional) Force a particular state
1405      */
1406     setActive : function(v) {
1407         
1408         this.el[v ? 'addClass' : 'removeClass']('active');
1409         this.pressed = v;
1410     },
1411      /**
1412      * toggles the current active state 
1413      */
1414     toggleActive : function(e)
1415     {
1416         this.setActive(!this.pressed); // this modifies pressed...
1417         this.fireEvent('toggle', this, e, this.pressed);
1418     },
1419      /**
1420      * get the current active state
1421      * @return {boolean} true if it's active
1422      */
1423     isActive : function()
1424     {
1425         return this.el.hasClass('active');
1426     },
1427     /**
1428      * set the text of the first selected button
1429      */
1430     setText : function(str)
1431     {
1432         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1433     },
1434     /**
1435      * get the text of the first selected button
1436      */
1437     getText : function()
1438     {
1439         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1440     },
1441     
1442     setWeight : function(str)
1443     {
1444         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1446         this.weight = str;
1447         var outline = this.outline ? 'outline-' : '';
1448         if (str == 'default') {
1449             this.el.addClass('btn-default btn-outline-secondary');        
1450             return;
1451         }
1452         this.el.addClass('btn-' + outline + str);        
1453     }
1454     
1455     
1456 });
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1458
1459 Roo.bootstrap.Button.weights = [
1460     'default',
1461     'secondary' ,
1462     'primary',
1463     'success',
1464     'info',
1465     'warning',
1466     'danger',
1467     'link',
1468     'light',
1469     'dark'              
1470    
1471 ];/*
1472  * - LGPL
1473  *
1474  * column
1475  * 
1476  */
1477
1478 /**
1479  * @class Roo.bootstrap.Column
1480  * @extends Roo.bootstrap.Component
1481  * @children Roo.bootstrap.Component
1482  * Bootstrap Column class
1483  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1491  *
1492  * 
1493  * @cfg {Boolean} hidden (true|false) hide the element
1494  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495  * @cfg {String} fa (ban|check|...) font awesome icon
1496  * @cfg {Number} fasize (1|2|....) font awsome size
1497
1498  * @cfg {String} icon (info-sign|check|...) glyphicon name
1499
1500  * @cfg {String} html content of column.
1501  * 
1502  * @constructor
1503  * Create a new Column
1504  * @param {Object} config The config object
1505  */
1506
1507 Roo.bootstrap.Column = function(config){
1508     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1509 };
1510
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1512     
1513     xs: false,
1514     sm: false,
1515     md: false,
1516     lg: false,
1517     xsoff: false,
1518     smoff: false,
1519     mdoff: false,
1520     lgoff: false,
1521     html: '',
1522     offset: 0,
1523     alert: false,
1524     fa: false,
1525     icon : false,
1526     hidden : false,
1527     fasize : 1,
1528     
1529     getAutoCreate : function(){
1530         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1531         
1532         cfg = {
1533             tag: 'div',
1534             cls: 'column'
1535         };
1536         
1537         var settings=this;
1538         var sizes =   ['xs','sm','md','lg'];
1539         sizes.map(function(size ,ix){
1540             //Roo.log( size + ':' + settings[size]);
1541             
1542             if (settings[size+'off'] !== false) {
1543                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1544             }
1545             
1546             if (settings[size] === false) {
1547                 return;
1548             }
1549             
1550             if (!settings[size]) { // 0 = hidden
1551                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1552                 // bootsrap4
1553                 for (var i = ix; i > -1; i--) {
1554                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1555                 }
1556                 
1557                 
1558                 return;
1559             }
1560             cfg.cls += ' col-' + size + '-' + settings[size] + (
1561                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1562             );
1563             
1564         });
1565         
1566         if (this.hidden) {
1567             cfg.cls += ' hidden';
1568         }
1569         
1570         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571             cfg.cls +=' alert alert-' + this.alert;
1572         }
1573         
1574         
1575         if (this.html.length) {
1576             cfg.html = this.html;
1577         }
1578         if (this.fa) {
1579             var fasize = '';
1580             if (this.fasize > 1) {
1581                 fasize = ' fa-' + this.fasize + 'x';
1582             }
1583             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1584             
1585             
1586         }
1587         if (this.icon) {
1588             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1589         }
1590         
1591         return cfg;
1592     }
1593    
1594 });
1595
1596  
1597
1598  /*
1599  * - LGPL
1600  *
1601  * page container.
1602  * 
1603  */
1604
1605
1606 /**
1607  * @class Roo.bootstrap.Container
1608  * @extends Roo.bootstrap.Component
1609  * @children Roo.bootstrap.Component
1610  * @parent builder
1611  * Bootstrap Container class
1612  * @cfg {Boolean} jumbotron is it a jumbotron element
1613  * @cfg {String} html content of element
1614  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1616  * @cfg {String} header content of header (for panel)
1617  * @cfg {String} footer content of footer (for panel)
1618  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619  * @cfg {String} tag (header|aside|section) type of HTML tag.
1620  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621  * @cfg {String} fa font awesome icon
1622  * @cfg {String} icon (info-sign|check|...) glyphicon name
1623  * @cfg {Boolean} hidden (true|false) hide the element
1624  * @cfg {Boolean} expandable (true|false) default false
1625  * @cfg {Boolean} expanded (true|false) default true
1626  * @cfg {String} rheader contet on the right of header
1627  * @cfg {Boolean} clickable (true|false) default false
1628
1629  *     
1630  * @constructor
1631  * Create a new Container
1632  * @param {Object} config The config object
1633  */
1634
1635 Roo.bootstrap.Container = function(config){
1636     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1637     
1638     this.addEvents({
1639         // raw events
1640          /**
1641          * @event expand
1642          * After the panel has been expand
1643          * 
1644          * @param {Roo.bootstrap.Container} this
1645          */
1646         "expand" : true,
1647         /**
1648          * @event collapse
1649          * After the panel has been collapsed
1650          * 
1651          * @param {Roo.bootstrap.Container} this
1652          */
1653         "collapse" : true,
1654         /**
1655          * @event click
1656          * When a element is chick
1657          * @param {Roo.bootstrap.Container} this
1658          * @param {Roo.EventObject} e
1659          */
1660         "click" : true
1661     });
1662 };
1663
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1665     
1666     jumbotron : false,
1667     well: '',
1668     panel : '',
1669     header: '',
1670     footer : '',
1671     sticky: '',
1672     tag : false,
1673     alert : false,
1674     fa: false,
1675     icon : false,
1676     expandable : false,
1677     rheader : '',
1678     expanded : true,
1679     clickable: false,
1680   
1681      
1682     getChildContainer : function() {
1683         
1684         if(!this.el){
1685             return false;
1686         }
1687         
1688         if (this.panel.length) {
1689             return this.el.select('.panel-body',true).first();
1690         }
1691         
1692         return this.el;
1693     },
1694     
1695     
1696     getAutoCreate : function(){
1697         
1698         var cfg = {
1699             tag : this.tag || 'div',
1700             html : '',
1701             cls : ''
1702         };
1703         if (this.jumbotron) {
1704             cfg.cls = 'jumbotron';
1705         }
1706         
1707         
1708         
1709         // - this is applied by the parent..
1710         //if (this.cls) {
1711         //    cfg.cls = this.cls + '';
1712         //}
1713         
1714         if (this.sticky.length) {
1715             
1716             var bd = Roo.get(document.body);
1717             if (!bd.hasClass('bootstrap-sticky')) {
1718                 bd.addClass('bootstrap-sticky');
1719                 Roo.select('html',true).setStyle('height', '100%');
1720             }
1721              
1722             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1723         }
1724         
1725         
1726         if (this.well.length) {
1727             switch (this.well) {
1728                 case 'lg':
1729                 case 'sm':
1730                     cfg.cls +=' well well-' +this.well;
1731                     break;
1732                 default:
1733                     cfg.cls +=' well';
1734                     break;
1735             }
1736         }
1737         
1738         if (this.hidden) {
1739             cfg.cls += ' hidden';
1740         }
1741         
1742         
1743         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744             cfg.cls +=' alert alert-' + this.alert;
1745         }
1746         
1747         var body = cfg;
1748         
1749         if (this.panel.length) {
1750             cfg.cls += ' panel panel-' + this.panel;
1751             cfg.cn = [];
1752             if (this.header.length) {
1753                 
1754                 var h = [];
1755                 
1756                 if(this.expandable){
1757                     
1758                     cfg.cls = cfg.cls + ' expandable';
1759                     
1760                     h.push({
1761                         tag: 'i',
1762                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1763                     });
1764                     
1765                 }
1766                 
1767                 h.push(
1768                     {
1769                         tag: 'span',
1770                         cls : 'panel-title',
1771                         html : (this.expandable ? '&nbsp;' : '') + this.header
1772                     },
1773                     {
1774                         tag: 'span',
1775                         cls: 'panel-header-right',
1776                         html: this.rheader
1777                     }
1778                 );
1779                 
1780                 cfg.cn.push({
1781                     cls : 'panel-heading',
1782                     style : this.expandable ? 'cursor: pointer' : '',
1783                     cn : h
1784                 });
1785                 
1786             }
1787             
1788             body = false;
1789             cfg.cn.push({
1790                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1791                 html : this.html
1792             });
1793             
1794             
1795             if (this.footer.length) {
1796                 cfg.cn.push({
1797                     cls : 'panel-footer',
1798                     html : this.footer
1799                     
1800                 });
1801             }
1802             
1803         }
1804         
1805         if (body) {
1806             body.html = this.html || cfg.html;
1807             // prefix with the icons..
1808             if (this.fa) {
1809                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1810             }
1811             if (this.icon) {
1812                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1813             }
1814             
1815             
1816         }
1817         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818             cfg.cls =  'container';
1819         }
1820         
1821         return cfg;
1822     },
1823     
1824     initEvents: function() 
1825     {
1826         if(this.expandable){
1827             var headerEl = this.headerEl();
1828         
1829             if(headerEl){
1830                 headerEl.on('click', this.onToggleClick, this);
1831             }
1832         }
1833         
1834         if(this.clickable){
1835             this.el.on('click', this.onClick, this);
1836         }
1837         
1838     },
1839     
1840     onToggleClick : function()
1841     {
1842         var headerEl = this.headerEl();
1843         
1844         if(!headerEl){
1845             return;
1846         }
1847         
1848         if(this.expanded){
1849             this.collapse();
1850             return;
1851         }
1852         
1853         this.expand();
1854     },
1855     
1856     expand : function()
1857     {
1858         if(this.fireEvent('expand', this)) {
1859             
1860             this.expanded = true;
1861             
1862             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1863             
1864             this.el.select('.panel-body',true).first().removeClass('hide');
1865             
1866             var toggleEl = this.toggleEl();
1867
1868             if(!toggleEl){
1869                 return;
1870             }
1871
1872             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1873         }
1874         
1875     },
1876     
1877     collapse : function()
1878     {
1879         if(this.fireEvent('collapse', this)) {
1880             
1881             this.expanded = false;
1882             
1883             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884             this.el.select('.panel-body',true).first().addClass('hide');
1885         
1886             var toggleEl = this.toggleEl();
1887
1888             if(!toggleEl){
1889                 return;
1890             }
1891
1892             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1893         }
1894     },
1895     
1896     toggleEl : function()
1897     {
1898         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1899             return;
1900         }
1901         
1902         return this.el.select('.panel-heading .fa',true).first();
1903     },
1904     
1905     headerEl : function()
1906     {
1907         if(!this.el || !this.panel.length || !this.header.length){
1908             return;
1909         }
1910         
1911         return this.el.select('.panel-heading',true).first()
1912     },
1913     
1914     bodyEl : function()
1915     {
1916         if(!this.el || !this.panel.length){
1917             return;
1918         }
1919         
1920         return this.el.select('.panel-body',true).first()
1921     },
1922     
1923     titleEl : function()
1924     {
1925         if(!this.el || !this.panel.length || !this.header.length){
1926             return;
1927         }
1928         
1929         return this.el.select('.panel-title',true).first();
1930     },
1931     
1932     setTitle : function(v)
1933     {
1934         var titleEl = this.titleEl();
1935         
1936         if(!titleEl){
1937             return;
1938         }
1939         
1940         titleEl.dom.innerHTML = v;
1941     },
1942     
1943     getTitle : function()
1944     {
1945         
1946         var titleEl = this.titleEl();
1947         
1948         if(!titleEl){
1949             return '';
1950         }
1951         
1952         return titleEl.dom.innerHTML;
1953     },
1954     
1955     setRightTitle : function(v)
1956     {
1957         var t = this.el.select('.panel-header-right',true).first();
1958         
1959         if(!t){
1960             return;
1961         }
1962         
1963         t.dom.innerHTML = v;
1964     },
1965     
1966     onClick : function(e)
1967     {
1968         e.preventDefault();
1969         
1970         this.fireEvent('click', this, e);
1971     }
1972 });
1973
1974  /**
1975  * @class Roo.bootstrap.Card
1976  * @extends Roo.bootstrap.Component
1977  * @children Roo.bootstrap.Component
1978  * @licence LGPL
1979  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1980  *
1981  *
1982  * possible... may not be implemented..
1983  * @cfg {String} header_image  src url of image.
1984  * @cfg {String|Object} header
1985  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1987  * 
1988  * @cfg {String} title
1989  * @cfg {String} subtitle
1990  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991  * @cfg {String} footer
1992  
1993  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1994  * 
1995  * @cfg {String} margin (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2002  *
2003  * @cfg {String} padding (0|1|2|3|4|5)
2004  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006  * @cfg {String} padding_left (0|1|2|3|4|5)
2007  * @cfg {String} padding_right (0|1|2|3|4|5)
2008  * @cfg {String} padding_x (0|1|2|3|4|5)
2009  * @cfg {String} padding_y (0|1|2|3|4|5)
2010  *
2011  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  
2017  * @config {Boolean} dragable  if this card can be dragged.
2018  * @config {String} drag_group  group for drag
2019  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2020  * @config {String} drop_group  group for drag
2021  * 
2022  * @config {Boolean} collapsable can the body be collapsed.
2023  * @config {Boolean} collapsed is the body collapsed when rendered...
2024  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025  * @config {Boolean} rotated is the body rotated when rendered...
2026  * 
2027  * @constructor
2028  * Create a new Container
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Card = function(config){
2033     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2034     
2035     this.addEvents({
2036          // raw events
2037         /**
2038          * @event drop
2039          * When a element a card is dropped
2040          * @param {Roo.bootstrap.Card} this
2041          *
2042          * 
2043          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044          * @param {String} position 'above' or 'below'
2045          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2046         
2047          */
2048         'drop' : true,
2049          /**
2050          * @event rotate
2051          * When a element a card is rotate
2052          * @param {Roo.bootstrap.Card} this
2053          * @param {Roo.Element} n the node being dropped?
2054          * @param {Boolean} rotate status
2055          */
2056         'rotate' : true,
2057         /**
2058          * @event cardover
2059          * When a card element is dragged over ready to drop (return false to block dropable)
2060          * @param {Roo.bootstrap.Card} this
2061          * @param {Object} data from dragdrop 
2062          */
2063          'cardover' : true
2064          
2065     });
2066 };
2067
2068
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2070     
2071     
2072     weight : '',
2073     
2074     margin: '', /// may be better in component?
2075     margin_top: '', 
2076     margin_bottom: '', 
2077     margin_left: '',
2078     margin_right: '',
2079     margin_x: '',
2080     margin_y: '',
2081     
2082     padding : '',
2083     padding_top: '', 
2084     padding_bottom: '', 
2085     padding_left: '',
2086     padding_right: '',
2087     padding_x: '',
2088     padding_y: '',
2089     
2090     display: '', 
2091     display_xs: '', 
2092     display_sm: '', 
2093     display_lg: '',
2094     display_xl: '',
2095  
2096     header_image  : '',
2097     header : '',
2098     header_size : 0,
2099     title : '',
2100     subtitle : '',
2101     html : '',
2102     footer: '',
2103
2104     collapsable : false,
2105     collapsed : false,
2106     rotateable : false,
2107     rotated : false,
2108     
2109     dragable : false,
2110     drag_group : false,
2111     dropable : false,
2112     drop_group : false,
2113     childContainer : false,
2114     dropEl : false, /// the dom placeholde element that indicates drop location.
2115     containerEl: false, // body container
2116     bodyEl: false, // card-body
2117     headerContainerEl : false, //
2118     headerEl : false,
2119     header_imageEl : false,
2120     
2121     
2122     layoutCls : function()
2123     {
2124         var cls = '';
2125         var t = this;
2126         Roo.log(this.margin_bottom.length);
2127         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2129             
2130             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2132             }
2133             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2135             }
2136         });
2137         
2138         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2141             }
2142         });
2143         
2144         // more generic support?
2145         if (this.hidden) {
2146             cls += ' d-none';
2147         }
2148         
2149         return cls;
2150     },
2151  
2152        // Roo.log("Call onRender: " + this.xtype);
2153         /*  We are looking at something like this.
2154 <div class="card">
2155     <img src="..." class="card-img-top" alt="...">
2156     <div class="card-body">
2157         <h5 class="card-title">Card title</h5>
2158          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2159
2160         >> this bit is really the body...
2161         <div> << we will ad dthis in hopefully it will not break shit.
2162         
2163         ** card text does not actually have any styling...
2164         
2165             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2166         
2167         </div> <<
2168           <a href="#" class="card-link">Card link</a>
2169           
2170     </div>
2171     <div class="card-footer">
2172         <small class="text-muted">Last updated 3 mins ago</small>
2173     </div>
2174 </div>
2175          */
2176     getAutoCreate : function(){
2177         
2178         var cfg = {
2179             tag : 'div',
2180             cls : 'card',
2181             cn : [ ]
2182         };
2183         
2184         if (this.weight.length && this.weight != 'light') {
2185             cfg.cls += ' text-white';
2186         } else {
2187             cfg.cls += ' text-dark'; // need as it's nested..
2188         }
2189         if (this.weight.length) {
2190             cfg.cls += ' bg-' + this.weight;
2191         }
2192         
2193         cfg.cls += ' ' + this.layoutCls(); 
2194         
2195         var hdr = false;
2196         var hdr_ctr = false;
2197         if (this.header.length) {
2198             hdr = {
2199                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201                 cn : []
2202             };
2203             cfg.cn.push(hdr);
2204             hdr_ctr = hdr;
2205         } else {
2206             hdr = {
2207                 tag : 'div',
2208                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209                 cn : []
2210             };
2211             cfg.cn.push(hdr);
2212             hdr_ctr = hdr;
2213         }
2214         if (this.collapsable) {
2215             hdr_ctr = {
2216             tag : 'a',
2217             cls : 'd-block user-select-none',
2218             cn: [
2219                     {
2220                         tag: 'i',
2221                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2222                     }
2223                    
2224                 ]
2225             };
2226             hdr.cn.push(hdr_ctr);
2227         }
2228         
2229         hdr_ctr.cn.push(        {
2230             tag: 'span',
2231             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2232             html : this.header
2233         });
2234         
2235         
2236         if (this.header_image.length) {
2237             cfg.cn.push({
2238                 tag : 'img',
2239                 cls : 'card-img-top',
2240                 src: this.header_image // escape?
2241             });
2242         } else {
2243             cfg.cn.push({
2244                     tag : 'div',
2245                     cls : 'card-img-top d-none' 
2246                 });
2247         }
2248             
2249         var body = {
2250             tag : 'div',
2251             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2252             cn : []
2253         };
2254         var obody = body;
2255         if (this.collapsable || this.rotateable) {
2256             obody = {
2257                 tag: 'div',
2258                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2259                 cn : [  body ]
2260             };
2261         }
2262         
2263         cfg.cn.push(obody);
2264         
2265         if (this.title.length) {
2266             body.cn.push({
2267                 tag : 'div',
2268                 cls : 'card-title',
2269                 src: this.title // escape?
2270             });
2271         }  
2272         
2273         if (this.subtitle.length) {
2274             body.cn.push({
2275                 tag : 'div',
2276                 cls : 'card-title',
2277                 src: this.subtitle // escape?
2278             });
2279         }
2280         
2281         body.cn.push({
2282             tag : 'div',
2283             cls : 'roo-card-body-ctr'
2284         });
2285         
2286         if (this.html.length) {
2287             body.cn.push({
2288                 tag: 'div',
2289                 html : this.html
2290             });
2291         }
2292         // fixme ? handle objects?
2293         
2294         if (this.footer.length) {
2295            
2296             cfg.cn.push({
2297                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2298                 html : this.footer
2299             });
2300             
2301         } else {
2302             cfg.cn.push({cls : 'card-footer d-none'});
2303         }
2304         
2305         // footer...
2306         
2307         return cfg;
2308     },
2309     
2310     
2311     getCardHeader : function()
2312     {
2313         var  ret = this.el.select('.card-header',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardFooter : function()
2321     {
2322         var  ret = this.el.select('.card-footer',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardImageTop : function()
2330     {
2331         var  ret = this.header_imageEl;
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335             
2336         return ret;
2337     },
2338     
2339     getChildContainer : function()
2340     {
2341         
2342         if(!this.el){
2343             return false;
2344         }
2345         return this.el.select('.roo-card-body-ctr',true).first();    
2346     },
2347     
2348     initEvents: function() 
2349     {
2350         this.bodyEl = this.el.select('.card-body',true).first(); 
2351         this.containerEl = this.getChildContainer();
2352         if(this.dragable){
2353             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354                     containerScroll: true,
2355                     ddGroup: this.drag_group || 'default_card_drag_group'
2356             });
2357             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2358         }
2359         if (this.dropable) {
2360             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361                 containerScroll: true,
2362                 ddGroup: this.drop_group || 'default_card_drag_group'
2363             });
2364             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369         }
2370         
2371         if (this.collapsable) {
2372             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2373         }
2374         if (this.rotateable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2376         }
2377         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2378          
2379         this.footerEl = this.el.select('.card-footer',true).first();
2380         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382         this.headerEl = this.el.select('.card-header',true).first();
2383         
2384         if (this.rotated) {
2385             this.el.addClass('roo-card-rotated');
2386             this.fireEvent('rotate', this, true);
2387         }
2388         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2389         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390         
2391     },
2392     getDragData : function(e)
2393     {
2394         var target = this.getEl();
2395         if (target) {
2396             //this.handleSelection(e);
2397             
2398             var dragData = {
2399                 source: this,
2400                 copy: false,
2401                 nodes: this.getEl(),
2402                 records: []
2403             };
2404             
2405             
2406             dragData.ddel = target.dom ;    // the div element
2407             Roo.log(target.getWidth( ));
2408             dragData.ddel.style.width = target.getWidth() + 'px';
2409             
2410             return dragData;
2411         }
2412         return false;
2413     },
2414     /**
2415     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2416     *    whole Element becomes the target, and this causes the drop gesture to append.
2417     *
2418     *    Returns an object:
2419     *     {
2420            
2421            position : 'below' or 'above'
2422            card  : relateive to card OBJECT (or true for no cards listed)
2423            items_n : relative to nth item in list
2424            card_n : relative to  nth card in list
2425     }
2426     *
2427     *    
2428     */
2429     getTargetFromEvent : function(e, dragged_card_el)
2430     {
2431         var target = e.getTarget();
2432         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433             target = target.parentNode;
2434         }
2435         
2436         var ret = {
2437             position: '',
2438             cards : [],
2439             card_n : -1,
2440             items_n : -1,
2441             card : false 
2442         };
2443         
2444         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445         // see if target is one of the 'cards'...
2446         
2447         
2448         //Roo.log(this.items.length);
2449         var pos = false;
2450         
2451         var last_card_n = 0;
2452         var cards_len  = 0;
2453         for (var i = 0;i< this.items.length;i++) {
2454             
2455             if (!this.items[i].el.hasClass('card')) {
2456                  continue;
2457             }
2458             pos = this.getDropPoint(e, this.items[i].el.dom);
2459             
2460             cards_len = ret.cards.length;
2461             //Roo.log(this.items[i].el.dom.id);
2462             ret.cards.push(this.items[i]);
2463             last_card_n  = i;
2464             if (ret.card_n < 0 && pos == 'above') {
2465                 ret.position = cards_len > 0 ? 'below' : pos;
2466                 ret.items_n = i > 0 ? i - 1 : 0;
2467                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2468                 ret.card = ret.cards[ret.card_n];
2469             }
2470         }
2471         if (!ret.cards.length) {
2472             ret.card = true;
2473             ret.position = 'below';
2474             ret.items_n;
2475             return ret;
2476         }
2477         // could not find a card.. stick it at the end..
2478         if (ret.card_n < 0) {
2479             ret.card_n = last_card_n;
2480             ret.card = ret.cards[last_card_n];
2481             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482             ret.position = 'below';
2483         }
2484         
2485         if (this.items[ret.items_n].el == dragged_card_el) {
2486             return false;
2487         }
2488         
2489         if (ret.position == 'below') {
2490             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2491             
2492             if (card_after  && card_after.el == dragged_card_el) {
2493                 return false;
2494             }
2495             return ret;
2496         }
2497         
2498         // its's after ..
2499         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2500         
2501         if (card_before  && card_before.el == dragged_card_el) {
2502             return false;
2503         }
2504         
2505         return ret;
2506     },
2507     
2508     onNodeEnter : function(n, dd, e, data){
2509         return false;
2510     },
2511     onNodeOver : function(n, dd, e, data)
2512     {
2513        
2514         var target_info = this.getTargetFromEvent(e,data.source.el);
2515         if (target_info === false) {
2516             this.dropPlaceHolder('hide');
2517             return false;
2518         }
2519         Roo.log(['getTargetFromEvent', target_info ]);
2520         
2521         
2522         if (this.fireEvent('cardover', this, [ data ]) === false) {
2523             return false;
2524         }
2525         
2526         this.dropPlaceHolder('show', target_info,data);
2527         
2528         return false; 
2529     },
2530     onNodeOut : function(n, dd, e, data){
2531         this.dropPlaceHolder('hide');
2532      
2533     },
2534     onNodeDrop : function(n, dd, e, data)
2535     {
2536         
2537         // call drop - return false if
2538         
2539         // this could actually fail - if the Network drops..
2540         // we will ignore this at present..- client should probably reload
2541         // the whole set of cards if stuff like that fails.
2542         
2543         
2544         var info = this.getTargetFromEvent(e,data.source.el);
2545         if (info === false) {
2546             return false;
2547         }
2548         this.dropPlaceHolder('hide');
2549   
2550           
2551     
2552         this.acceptCard(data.source, info.position, info.card, info.items_n);
2553         return true;
2554          
2555     },
2556     firstChildCard : function()
2557     {
2558         for (var i = 0;i< this.items.length;i++) {
2559             
2560             if (!this.items[i].el.hasClass('card')) {
2561                  continue;
2562             }
2563             return this.items[i];
2564         }
2565         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2566     },
2567     /**
2568      * accept card
2569      *
2570      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2571      */
2572     acceptCard : function(move_card,  position, next_to_card )
2573     {
2574         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2575             return false;
2576         }
2577         
2578         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2579         
2580         move_card.parent().removeCard(move_card);
2581         
2582         
2583         var dom = move_card.el.dom;
2584         dom.style.width = ''; // clear with - which is set by drag.
2585         
2586         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587             var cardel = next_to_card.el.dom;
2588             
2589             if (position == 'above' ) {
2590                 cardel.parentNode.insertBefore(dom, cardel);
2591             } else if (cardel.nextSibling) {
2592                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2593             } else {
2594                 cardel.parentNode.append(dom);
2595             }
2596         } else {
2597             // card container???
2598             this.containerEl.dom.append(dom);
2599         }
2600         
2601         //FIXME HANDLE card = true 
2602         
2603         // add this to the correct place in items.
2604         
2605         // remove Card from items.
2606         
2607        
2608         if (this.items.length) {
2609             var nitems = [];
2610             //Roo.log([info.items_n, info.position, this.items.length]);
2611             for (var i =0; i < this.items.length; i++) {
2612                 if (i == to_items_n && position == 'above') {
2613                     nitems.push(move_card);
2614                 }
2615                 nitems.push(this.items[i]);
2616                 if (i == to_items_n && position == 'below') {
2617                     nitems.push(move_card);
2618                 }
2619             }
2620             this.items = nitems;
2621             Roo.log(this.items);
2622         } else {
2623             this.items.push(move_card);
2624         }
2625         
2626         move_card.parentId = this.id;
2627         
2628         return true;
2629         
2630         
2631     },
2632     removeCard : function(c)
2633     {
2634         this.items = this.items.filter(function(e) { return e != c });
2635  
2636         var dom = c.el.dom;
2637         dom.parentNode.removeChild(dom);
2638         dom.style.width = ''; // clear with - which is set by drag.
2639         c.parentId = false;
2640         
2641     },
2642     
2643     /**    Decide whether to drop above or below a View node. */
2644     getDropPoint : function(e, n, dd)
2645     {
2646         if (dd) {
2647              return false;
2648         }
2649         if (n == this.containerEl.dom) {
2650             return "above";
2651         }
2652         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653         var c = t + (b - t) / 2;
2654         var y = Roo.lib.Event.getPageY(e);
2655         if(y <= c) {
2656             return "above";
2657         }else{
2658             return "below";
2659         }
2660     },
2661     onToggleCollapse : function(e)
2662         {
2663         if (this.collapsed) {
2664             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665             this.collapsableEl.addClass('show');
2666             this.collapsed = false;
2667             return;
2668         }
2669         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670         this.collapsableEl.removeClass('show');
2671         this.collapsed = true;
2672         
2673     
2674     },
2675     
2676     onToggleRotate : function(e)
2677     {
2678         this.collapsableEl.removeClass('show');
2679         this.footerEl.removeClass('d-none');
2680         this.el.removeClass('roo-card-rotated');
2681         this.el.removeClass('d-none');
2682         if (this.rotated) {
2683             
2684             this.collapsableEl.addClass('show');
2685             this.rotated = false;
2686             this.fireEvent('rotate', this, this.rotated);
2687             return;
2688         }
2689         this.el.addClass('roo-card-rotated');
2690         this.footerEl.addClass('d-none');
2691         this.el.select('.roo-collapsable').removeClass('show');
2692         
2693         this.rotated = true;
2694         this.fireEvent('rotate', this, this.rotated);
2695     
2696     },
2697     
2698     dropPlaceHolder: function (action, info, data)
2699     {
2700         if (this.dropEl === false) {
2701             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2702             cls : 'd-none'
2703             },true);
2704         }
2705         this.dropEl.removeClass(['d-none', 'd-block']);        
2706         if (action == 'hide') {
2707             
2708             this.dropEl.addClass('d-none');
2709             return;
2710         }
2711         // FIXME - info.card == true!!!
2712         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2713         
2714         if (info.card !== true) {
2715             var cardel = info.card.el.dom;
2716             
2717             if (info.position == 'above') {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719             } else if (cardel.nextSibling) {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2721             } else {
2722                 cardel.parentNode.append(this.dropEl.dom);
2723             }
2724         } else {
2725             // card container???
2726             this.containerEl.dom.append(this.dropEl.dom);
2727         }
2728         
2729         this.dropEl.addClass('d-block roo-card-dropzone');
2730         
2731         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2732         
2733         
2734     
2735     
2736     
2737     },
2738     setHeaderText: function(html)
2739     {
2740         this.header = html;
2741         if (this.headerContainerEl) {
2742             this.headerContainerEl.dom.innerHTML = html;
2743         }
2744     },
2745     onHeaderImageLoad : function(ev, he)
2746     {
2747         if (!this.header_image_fit_square) {
2748             return;
2749         }
2750         
2751         var hw = he.naturalHeight / he.naturalWidth;
2752         // wide image = < 0
2753         // tall image = > 1
2754         //var w = he.dom.naturalWidth;
2755         var ww = he.width;
2756         he.style.left =  0;
2757         he.style.position =  'relative';
2758         if (hw > 1) {
2759             var nw = (ww * (1/hw));
2760             Roo.get(he).setSize( ww * (1/hw),  ww);
2761             he.style.left =  ((ww - nw)/ 2) + 'px';
2762             he.style.position =  'relative';
2763         }
2764
2765     }
2766
2767     
2768 });
2769
2770 /*
2771  * - LGPL
2772  *
2773  * Card header - holder for the card header elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardHeader
2779  * @extends Roo.bootstrap.Element
2780  * @parent Roo.bootstrap.Card
2781  * @children Roo.bootstrap.Component
2782  * Bootstrap CardHeader class
2783  * @constructor
2784  * Create a new Card Header - that you can embed children into
2785  * @param {Object} config The config object
2786  */
2787
2788 Roo.bootstrap.CardHeader = function(config){
2789     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 };
2791
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2793     
2794     
2795     container_method : 'getCardHeader' 
2796     
2797      
2798     
2799     
2800    
2801 });
2802
2803  
2804
2805  /*
2806  * - LGPL
2807  *
2808  * Card footer - holder for the card footer elements.
2809  * 
2810  */
2811
2812 /**
2813  * @class Roo.bootstrap.CardFooter
2814  * @extends Roo.bootstrap.Element
2815  * @parent Roo.bootstrap.Card
2816  * @children Roo.bootstrap.Component
2817  * Bootstrap CardFooter class
2818  * 
2819  * @constructor
2820  * Create a new Card Footer - that you can embed children into
2821  * @param {Object} config The config object
2822  */
2823
2824 Roo.bootstrap.CardFooter = function(config){
2825     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2826 };
2827
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2829     
2830     
2831     container_method : 'getCardFooter' 
2832     
2833      
2834     
2835     
2836    
2837 });
2838
2839  
2840
2841  /*
2842  * - LGPL
2843  *
2844  * Card header - holder for the card header elements.
2845  * 
2846  */
2847
2848 /**
2849  * @class Roo.bootstrap.CardImageTop
2850  * @extends Roo.bootstrap.Element
2851  * @parent Roo.bootstrap.Card
2852  * @children Roo.bootstrap.Component
2853  * Bootstrap CardImageTop class
2854  * 
2855  * @constructor
2856  * Create a new Card Image Top container
2857  * @param {Object} config The config object
2858  */
2859
2860 Roo.bootstrap.CardImageTop = function(config){
2861     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2862 };
2863
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2865     
2866    
2867     container_method : 'getCardImageTop' 
2868     
2869      
2870     
2871    
2872 });
2873
2874  
2875
2876  
2877 /*
2878 * Licence: LGPL
2879 */
2880
2881 /**
2882  * @class Roo.bootstrap.ButtonUploader
2883  * @extends Roo.bootstrap.Button
2884  * Bootstrap Button Uploader class - it's a button which when you add files to it
2885  *
2886  * 
2887  * @cfg {Number} errorTimeout default 3000
2888  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2889  * @cfg {Array}  html The button text.
2890  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2891  *
2892  * @constructor
2893  * Create a new CardUploader
2894  * @param {Object} config The config object
2895  */
2896
2897 Roo.bootstrap.ButtonUploader = function(config){
2898     
2899  
2900     
2901     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2902     
2903      
2904      this.addEvents({
2905          // raw events
2906         /**
2907          * @event beforeselect
2908          * When button is pressed, before show upload files dialog is shown
2909          * @param {Roo.bootstrap.UploaderButton} this
2910          *
2911          */
2912         'beforeselect' : true,
2913          /**
2914          * @event fired when files have been selected, 
2915          * When a the download link is clicked
2916          * @param {Roo.bootstrap.UploaderButton} this
2917          * @param {Array} Array of files that have been uploaded
2918          */
2919         'uploaded' : true
2920         
2921     });
2922 };
2923  
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2925     
2926      
2927     errorTimeout : 3000,
2928      
2929     images : false,
2930    
2931     fileCollection : false,
2932     allowBlank : true,
2933     
2934     multiple : true,
2935     
2936     getAutoCreate : function()
2937     {
2938        
2939         
2940         return  {
2941             cls :'div' ,
2942             cn : [
2943                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2944             ]
2945         };
2946            
2947          
2948     },
2949      
2950    
2951     initEvents : function()
2952     {
2953         
2954         Roo.bootstrap.Button.prototype.initEvents.call(this);
2955         
2956         
2957         
2958         
2959         
2960         this.urlAPI = (window.createObjectURL && window) || 
2961                                 (window.URL && URL.revokeObjectURL && URL) || 
2962                                 (window.webkitURL && webkitURL);
2963                         
2964         var im = {
2965             tag: 'input',
2966             type : 'file',
2967             cls : 'd-none  roo-card-upload-selector' 
2968           
2969         };
2970         if (this.multiple) {
2971             im.multiple = 'multiple';
2972         }
2973         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2974        
2975         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2976         
2977         this.selectorEl.on('change', this.onFileSelected, this);
2978          
2979          
2980        
2981     },
2982     
2983    
2984     onClick : function(e)
2985     {
2986         e.preventDefault();
2987         
2988         if ( this.fireEvent('beforeselect', this) === false) {
2989             return;
2990         }
2991          
2992         this.selectorEl.dom.click();
2993          
2994     },
2995     
2996     onFileSelected : function(e)
2997     {
2998         e.preventDefault();
2999         
3000         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3001             return;
3002         }
3003         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004         this.selectorEl.dom.value  = '';// hopefully reset..
3005         
3006         this.fireEvent('uploaded', this,  files );
3007         
3008     },
3009     
3010        
3011    
3012     
3013     /**
3014      * addCard - add an Attachment to the uploader
3015      * @param data - the data about the image to upload
3016      *
3017      * {
3018           id : 123
3019           title : "Title of file",
3020           is_uploaded : false,
3021           src : "http://.....",
3022           srcfile : { the File upload object },
3023           mimetype : file.type,
3024           preview : false,
3025           is_deleted : 0
3026           .. any other data...
3027         }
3028      *
3029      * 
3030     */
3031      
3032     reset: function()
3033     {
3034          
3035          this.selectorEl
3036     } 
3037     
3038     
3039     
3040     
3041 });
3042  /*
3043  * - LGPL
3044  *
3045  * image
3046  * 
3047  */
3048
3049
3050 /**
3051  * @class Roo.bootstrap.Img
3052  * @extends Roo.bootstrap.Component
3053  * Bootstrap Img class
3054  * @cfg {Boolean} imgResponsive false | true
3055  * @cfg {String} border rounded | circle | thumbnail
3056  * @cfg {String} src image source
3057  * @cfg {String} alt image alternative text
3058  * @cfg {String} href a tag href
3059  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060  * @cfg {String} xsUrl xs image source
3061  * @cfg {String} smUrl sm image source
3062  * @cfg {String} mdUrl md image source
3063  * @cfg {String} lgUrl lg image source
3064  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3065  * 
3066  * @constructor
3067  * Create a new Input
3068  * @param {Object} config The config object
3069  */
3070
3071 Roo.bootstrap.Img = function(config){
3072     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3073     
3074     this.addEvents({
3075         // img events
3076         /**
3077          * @event click
3078          * The img click event for the img.
3079          * @param {Roo.EventObject} e
3080          */
3081         "click" : true,
3082         /**
3083          * @event load
3084          * The when any image loads
3085          * @param {Roo.EventObject} e
3086          */
3087         "load" : true
3088     });
3089 };
3090
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3092     
3093     imgResponsive: true,
3094     border: '',
3095     src: 'about:blank',
3096     href: false,
3097     target: false,
3098     xsUrl: '',
3099     smUrl: '',
3100     mdUrl: '',
3101     lgUrl: '',
3102     backgroundContain : false,
3103
3104     getAutoCreate : function()
3105     {   
3106         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107             return this.createSingleImg();
3108         }
3109         
3110         var cfg = {
3111             tag: 'div',
3112             cls: 'roo-image-responsive-group',
3113             cn: []
3114         };
3115         var _this = this;
3116         
3117         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3118             
3119             if(!_this[size + 'Url']){
3120                 return;
3121             }
3122             
3123             var img = {
3124                 tag: 'img',
3125                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126                 html: _this.html || cfg.html,
3127                 src: _this[size + 'Url']
3128             };
3129             
3130             img.cls += ' roo-image-responsive-' + size;
3131             
3132             var s = ['xs', 'sm', 'md', 'lg'];
3133             
3134             s.splice(s.indexOf(size), 1);
3135             
3136             Roo.each(s, function(ss){
3137                 img.cls += ' hidden-' + ss;
3138             });
3139             
3140             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141                 cfg.cls += ' img-' + _this.border;
3142             }
3143             
3144             if(_this.alt){
3145                 cfg.alt = _this.alt;
3146             }
3147             
3148             if(_this.href){
3149                 var a = {
3150                     tag: 'a',
3151                     href: _this.href,
3152                     cn: [
3153                         img
3154                     ]
3155                 };
3156
3157                 if(this.target){
3158                     a.target = _this.target;
3159                 }
3160             }
3161             
3162             cfg.cn.push((_this.href) ? a : img);
3163             
3164         });
3165         
3166         return cfg;
3167     },
3168     
3169     createSingleImg : function()
3170     {
3171         var cfg = {
3172             tag: 'img',
3173             cls: (this.imgResponsive) ? 'img-responsive' : '',
3174             html : null,
3175             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3176         };
3177         
3178         if (this.backgroundContain) {
3179             cfg.cls += ' background-contain';
3180         }
3181         
3182         cfg.html = this.html || cfg.html;
3183         
3184         if (this.backgroundContain) {
3185             cfg.style="background-image: url(" + this.src + ')';
3186         } else {
3187             cfg.src = this.src || cfg.src;
3188         }
3189         
3190         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191             cfg.cls += ' img-' + this.border;
3192         }
3193         
3194         if(this.alt){
3195             cfg.alt = this.alt;
3196         }
3197         
3198         if(this.href){
3199             var a = {
3200                 tag: 'a',
3201                 href: this.href,
3202                 cn: [
3203                     cfg
3204                 ]
3205             };
3206             
3207             if(this.target){
3208                 a.target = this.target;
3209             }
3210             
3211         }
3212         
3213         return (this.href) ? a : cfg;
3214     },
3215     
3216     initEvents: function() 
3217     {
3218         if(!this.href){
3219             this.el.on('click', this.onClick, this);
3220         }
3221         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222             this.el.on('load', this.onImageLoad, this);
3223         } else {
3224             // not sure if this works.. not tested
3225             this.el.select('img', true).on('load', this.onImageLoad, this);
3226         }
3227         
3228     },
3229     
3230     onClick : function(e)
3231     {
3232         Roo.log('img onclick');
3233         this.fireEvent('click', this, e);
3234     },
3235     onImageLoad: function(e)
3236     {
3237         Roo.log('img load');
3238         this.fireEvent('load', this, e);
3239     },
3240     
3241     /**
3242      * Sets the url of the image - used to update it
3243      * @param {String} url the url of the image
3244      */
3245     
3246     setSrc : function(url)
3247     {
3248         this.src =  url;
3249         
3250         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251             if (this.backgroundContain) {
3252                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3253             } else {
3254                 this.el.dom.src =  url;
3255             }
3256             return;
3257         }
3258         
3259         this.el.select('img', true).first().dom.src =  url;
3260     }
3261     
3262     
3263    
3264 });
3265
3266  /*
3267  * - LGPL
3268  *
3269  * image
3270  * 
3271  */
3272
3273
3274 /**
3275  * @class Roo.bootstrap.Link
3276  * @extends Roo.bootstrap.Component
3277  * @children Roo.bootstrap.Component
3278  * Bootstrap Link Class (eg. '<a href>')
3279  
3280  * @cfg {String} alt image alternative text
3281  * @cfg {String} href a tag href
3282  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283  * @cfg {String} html the content of the link.
3284  * @cfg {String} anchor name for the anchor link
3285  * @cfg {String} fa - favicon
3286
3287  * @cfg {Boolean} preventDefault (true | false) default false
3288
3289  * 
3290  * @constructor
3291  * Create a new Input
3292  * @param {Object} config The config object
3293  */
3294
3295 Roo.bootstrap.Link = function(config){
3296     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3297     
3298     this.addEvents({
3299         // img events
3300         /**
3301          * @event click
3302          * The img click event for the img.
3303          * @param {Roo.EventObject} e
3304          */
3305         "click" : true
3306     });
3307 };
3308
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3310     
3311     href: false,
3312     target: false,
3313     preventDefault: false,
3314     anchor : false,
3315     alt : false,
3316     fa: false,
3317
3318
3319     getAutoCreate : function()
3320     {
3321         var html = this.html || '';
3322         
3323         if (this.fa !== false) {
3324             html = '<i class="fa fa-' + this.fa + '"></i>';
3325         }
3326         var cfg = {
3327             tag: 'a'
3328         };
3329         // anchor's do not require html/href...
3330         if (this.anchor === false) {
3331             cfg.html = html;
3332             cfg.href = this.href || '#';
3333         } else {
3334             cfg.name = this.anchor;
3335             if (this.html !== false || this.fa !== false) {
3336                 cfg.html = html;
3337             }
3338             if (this.href !== false) {
3339                 cfg.href = this.href;
3340             }
3341         }
3342         
3343         if(this.alt !== false){
3344             cfg.alt = this.alt;
3345         }
3346         
3347         
3348         if(this.target !== false) {
3349             cfg.target = this.target;
3350         }
3351         
3352         return cfg;
3353     },
3354     
3355     initEvents: function() {
3356         
3357         if(!this.href || this.preventDefault){
3358             this.el.on('click', this.onClick, this);
3359         }
3360     },
3361     
3362     onClick : function(e)
3363     {
3364         if(this.preventDefault){
3365             e.preventDefault();
3366         }
3367         //Roo.log('img onclick');
3368         this.fireEvent('click', this, e);
3369     }
3370    
3371 });
3372
3373  /*
3374  * - LGPL
3375  *
3376  * header
3377  * 
3378  */
3379
3380 /**
3381  * @class Roo.bootstrap.Header
3382  * @extends Roo.bootstrap.Component
3383  * @children Roo.bootstrap.Component
3384  * Bootstrap Header class
3385  *
3386  * 
3387  * @cfg {String} html content of header
3388  * @cfg {Number} level (1|2|3|4|5|6) default 1
3389  * 
3390  * @constructor
3391  * Create a new Header
3392  * @param {Object} config The config object
3393  */
3394
3395
3396 Roo.bootstrap.Header  = function(config){
3397     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3398 };
3399
3400 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3401     
3402     //href : false,
3403     html : false,
3404     level : 1,
3405     
3406     
3407     
3408     getAutoCreate : function(){
3409         
3410         
3411         
3412         var cfg = {
3413             tag: 'h' + (1 *this.level),
3414             html: this.html || ''
3415         } ;
3416         
3417         return cfg;
3418     }
3419    
3420 });
3421
3422  
3423
3424  /**
3425  * @class Roo.bootstrap.MenuMgr
3426  * @licence LGPL
3427  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3428  * @static
3429  */
3430 Roo.bootstrap.menu.Manager = function(){
3431    var menus, active, groups = {}, attached = false, lastShow = new Date();
3432
3433    // private - called when first menu is created
3434    function init(){
3435        menus = {};
3436        active = new Roo.util.MixedCollection();
3437        Roo.get(document).addKeyListener(27, function(){
3438            if(active.length > 0){
3439                hideAll();
3440            }
3441        });
3442    }
3443
3444    // private
3445    function hideAll(){
3446        if(active && active.length > 0){
3447            var c = active.clone();
3448            c.each(function(m){
3449                m.hide();
3450            });
3451        }
3452    }
3453
3454    // private
3455    function onHide(m){
3456        active.remove(m);
3457        if(active.length < 1){
3458            Roo.get(document).un("mouseup", onMouseDown);
3459             
3460            attached = false;
3461        }
3462    }
3463
3464    // private
3465    function onShow(m){
3466        var last = active.last();
3467        lastShow = new Date();
3468        active.add(m);
3469        if(!attached){
3470           Roo.get(document).on("mouseup", onMouseDown);
3471            
3472            attached = true;
3473        }
3474        if(m.parentMenu){
3475           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3476           m.parentMenu.activeChild = m;
3477        }else if(last && last.isVisible()){
3478           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3479        }
3480    }
3481
3482    // private
3483    function onBeforeHide(m){
3484        if(m.activeChild){
3485            m.activeChild.hide();
3486        }
3487        if(m.autoHideTimer){
3488            clearTimeout(m.autoHideTimer);
3489            delete m.autoHideTimer;
3490        }
3491    }
3492
3493    // private
3494    function onBeforeShow(m){
3495        var pm = m.parentMenu;
3496        if(!pm && !m.allowOtherMenus){
3497            hideAll();
3498        }else if(pm && pm.activeChild && active != m){
3499            pm.activeChild.hide();
3500        }
3501    }
3502
3503    // private this should really trigger on mouseup..
3504    function onMouseDown(e){
3505         Roo.log("on Mouse Up");
3506         
3507         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3508             Roo.log("MenuManager hideAll");
3509             hideAll();
3510             e.stopEvent();
3511         }
3512         
3513         
3514    }
3515
3516    // private
3517    function onBeforeCheck(mi, state){
3518        if(state){
3519            var g = groups[mi.group];
3520            for(var i = 0, l = g.length; i < l; i++){
3521                if(g[i] != mi){
3522                    g[i].setChecked(false);
3523                }
3524            }
3525        }
3526    }
3527
3528    return {
3529
3530        /**
3531         * Hides all menus that are currently visible
3532         */
3533        hideAll : function(){
3534             hideAll();  
3535        },
3536
3537        // private
3538        register : function(menu){
3539            if(!menus){
3540                init();
3541            }
3542            menus[menu.id] = menu;
3543            menu.on("beforehide", onBeforeHide);
3544            menu.on("hide", onHide);
3545            menu.on("beforeshow", onBeforeShow);
3546            menu.on("show", onShow);
3547            var g = menu.group;
3548            if(g && menu.events["checkchange"]){
3549                if(!groups[g]){
3550                    groups[g] = [];
3551                }
3552                groups[g].push(menu);
3553                menu.on("checkchange", onCheck);
3554            }
3555        },
3556
3557         /**
3558          * Returns a {@link Roo.menu.Menu} object
3559          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3560          * be used to generate and return a new Menu instance.
3561          */
3562        get : function(menu){
3563            if(typeof menu == "string"){ // menu id
3564                return menus[menu];
3565            }else if(menu.events){  // menu instance
3566                return menu;
3567            }
3568            /*else if(typeof menu.length == 'number'){ // array of menu items?
3569                return new Roo.bootstrap.Menu({items:menu});
3570            }else{ // otherwise, must be a config
3571                return new Roo.bootstrap.Menu(menu);
3572            }
3573            */
3574            return false;
3575        },
3576
3577        // private
3578        unregister : function(menu){
3579            delete menus[menu.id];
3580            menu.un("beforehide", onBeforeHide);
3581            menu.un("hide", onHide);
3582            menu.un("beforeshow", onBeforeShow);
3583            menu.un("show", onShow);
3584            var g = menu.group;
3585            if(g && menu.events["checkchange"]){
3586                groups[g].remove(menu);
3587                menu.un("checkchange", onCheck);
3588            }
3589        },
3590
3591        // private
3592        registerCheckable : function(menuItem){
3593            var g = menuItem.group;
3594            if(g){
3595                if(!groups[g]){
3596                    groups[g] = [];
3597                }
3598                groups[g].push(menuItem);
3599                menuItem.on("beforecheckchange", onBeforeCheck);
3600            }
3601        },
3602
3603        // private
3604        unregisterCheckable : function(menuItem){
3605            var g = menuItem.group;
3606            if(g){
3607                groups[g].remove(menuItem);
3608                menuItem.un("beforecheckchange", onBeforeCheck);
3609            }
3610        }
3611    };
3612 }(); 
3613 /**
3614  * @class Roo.bootstrap.menu.Menu
3615  * @extends Roo.bootstrap.Component
3616  * @licence LGPL
3617  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3618  * @parent none
3619  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3620  * 
3621  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3622  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3623  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3624  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3625 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3626 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3627  
3628  * @constructor
3629  * Create a new Menu
3630  * @param {Object} config The config objectQ
3631  */
3632
3633
3634 Roo.bootstrap.menu.Menu = function(config){
3635     
3636     if (config.type == 'treeview') {
3637         // normally menu's are drawn attached to the document to handle layering etc..
3638         // however treeview (used by the docs menu is drawn into the parent element)
3639         this.container_method = 'getChildContainer'; 
3640     }
3641     
3642     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3643     if (this.registerMenu && this.type != 'treeview')  {
3644         Roo.bootstrap.menu.Manager.register(this);
3645     }
3646     
3647     
3648     this.addEvents({
3649         /**
3650          * @event beforeshow
3651          * Fires before this menu is displayed (return false to block)
3652          * @param {Roo.menu.Menu} this
3653          */
3654         beforeshow : true,
3655         /**
3656          * @event beforehide
3657          * Fires before this menu is hidden (return false to block)
3658          * @param {Roo.menu.Menu} this
3659          */
3660         beforehide : true,
3661         /**
3662          * @event show
3663          * Fires after this menu is displayed
3664          * @param {Roo.menu.Menu} this
3665          */
3666         show : true,
3667         /**
3668          * @event hide
3669          * Fires after this menu is hidden
3670          * @param {Roo.menu.Menu} this
3671          */
3672         hide : true,
3673         /**
3674          * @event click
3675          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3676          * @param {Roo.menu.Menu} this
3677          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3678          * @param {Roo.EventObject} e
3679          */
3680         click : true,
3681         /**
3682          * @event mouseover
3683          * Fires when the mouse is hovering over this menu
3684          * @param {Roo.menu.Menu} this
3685          * @param {Roo.EventObject} e
3686          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3687          */
3688         mouseover : true,
3689         /**
3690          * @event mouseout
3691          * Fires when the mouse exits this menu
3692          * @param {Roo.menu.Menu} this
3693          * @param {Roo.EventObject} e
3694          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3695          */
3696         mouseout : true,
3697         /**
3698          * @event itemclick
3699          * Fires when a menu item contained in this menu is clicked
3700          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3701          * @param {Roo.EventObject} e
3702          */
3703         itemclick: true
3704     });
3705     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3706 };
3707
3708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3709     
3710    /// html : false,
3711    
3712     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3713     type: false,
3714     /**
3715      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3716      */
3717     registerMenu : true,
3718     
3719     menuItems :false, // stores the menu items..
3720     
3721     hidden:true,
3722         
3723     parentMenu : false,
3724     
3725     stopEvent : true,
3726     
3727     isLink : false,
3728     
3729     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3730     
3731     hideTrigger : false,
3732     
3733     align : 'tl-bl?',
3734     
3735     
3736     getChildContainer : function() {
3737         return this.el;  
3738     },
3739     
3740     getAutoCreate : function(){
3741          
3742         //if (['right'].indexOf(this.align)!==-1) {
3743         //    cfg.cn[1].cls += ' pull-right'
3744         //}
3745          
3746         var cfg = {
3747             tag : 'ul',
3748             cls : 'dropdown-menu shadow' ,
3749             style : 'z-index:1000'
3750             
3751         };
3752         
3753         if (this.type === 'submenu') {
3754             cfg.cls = 'submenu active';
3755         }
3756         if (this.type === 'treeview') {
3757             cfg.cls = 'treeview-menu';
3758         }
3759         
3760         return cfg;
3761     },
3762     initEvents : function() {
3763         
3764        // Roo.log("ADD event");
3765        // Roo.log(this.triggerEl.dom);
3766         if (this.triggerEl) {
3767             
3768             this.triggerEl.on('click', this.onTriggerClick, this);
3769             
3770             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3771             
3772             if (!this.hideTrigger) {
3773                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3774                     // dropdown toggle on the 'a' in BS4?
3775                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3776                 } else {
3777                     this.triggerEl.addClass('dropdown-toggle');
3778                 }
3779             }
3780         }
3781         
3782         if (Roo.isTouch) {
3783             this.el.on('touchstart'  , this.onTouch, this);
3784         }
3785         this.el.on('click' , this.onClick, this);
3786
3787         this.el.on("mouseover", this.onMouseOver, this);
3788         this.el.on("mouseout", this.onMouseOut, this);
3789         
3790     },
3791     
3792     findTargetItem : function(e)
3793     {
3794         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3795         if(!t){
3796             return false;
3797         }
3798         //Roo.log(t);         Roo.log(t.id);
3799         if(t && t.id){
3800             //Roo.log(this.menuitems);
3801             return this.menuitems.get(t.id);
3802             
3803             //return this.items.get(t.menuItemId);
3804         }
3805         
3806         return false;
3807     },
3808     
3809     onTouch : function(e) 
3810     {
3811         Roo.log("menu.onTouch");
3812         //e.stopEvent(); this make the user popdown broken
3813         this.onClick(e);
3814     },
3815     
3816     onClick : function(e)
3817     {
3818         Roo.log("menu.onClick");
3819         
3820         var t = this.findTargetItem(e);
3821         if(!t || t.isContainer){
3822             return;
3823         }
3824         Roo.log(e);
3825         /*
3826         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3827             if(t == this.activeItem && t.shouldDeactivate(e)){
3828                 this.activeItem.deactivate();
3829                 delete this.activeItem;
3830                 return;
3831             }
3832             if(t.canActivate){
3833                 this.setActiveItem(t, true);
3834             }
3835             return;
3836             
3837             
3838         }
3839         */
3840        
3841         Roo.log('pass click event');
3842         
3843         t.onClick(e);
3844         
3845         this.fireEvent("click", this, t, e);
3846         
3847         var _this = this;
3848         
3849         if(!t.href.length || t.href == '#'){
3850             (function() { _this.hide(); }).defer(100);
3851         }
3852         
3853     },
3854     
3855     onMouseOver : function(e){
3856         var t  = this.findTargetItem(e);
3857         //Roo.log(t);
3858         //if(t){
3859         //    if(t.canActivate && !t.disabled){
3860         //        this.setActiveItem(t, true);
3861         //    }
3862         //}
3863         
3864         this.fireEvent("mouseover", this, e, t);
3865     },
3866     isVisible : function(){
3867         return !this.hidden;
3868     },
3869     onMouseOut : function(e){
3870         var t  = this.findTargetItem(e);
3871         
3872         //if(t ){
3873         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3874         //        this.activeItem.deactivate();
3875         //        delete this.activeItem;
3876         //    }
3877         //}
3878         this.fireEvent("mouseout", this, e, t);
3879     },
3880     
3881     
3882     /**
3883      * Displays this menu relative to another element
3884      * @param {String/HTMLElement/Roo.Element} element The element to align to
3885      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3886      * the element (defaults to this.defaultAlign)
3887      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3888      */
3889     show : function(el, pos, parentMenu)
3890     {
3891         if (false === this.fireEvent("beforeshow", this)) {
3892             Roo.log("show canceled");
3893             return;
3894         }
3895         this.parentMenu = parentMenu;
3896         if(!this.el){
3897             this.render();
3898         }
3899         this.el.addClass('show'); // show otherwise we do not know how big we are..
3900          
3901         var xy = this.el.getAlignToXY(el, pos);
3902         
3903         // bl-tl << left align  below
3904         // tl-bl << left align 
3905         
3906         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3907             // if it goes to far to the right.. -> align left.
3908             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3909         }
3910         if(xy[0] < 0){
3911             // was left align - go right?
3912             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3913         }
3914         
3915         // goes down the bottom
3916         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3917            xy[1]  < 0 ){
3918             var a = this.align.replace('?', '').split('-');
3919             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3920             
3921         }
3922         
3923         this.showAt(  xy , parentMenu, false);
3924     },
3925      /**
3926      * Displays this menu at a specific xy position
3927      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3928      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3929      */
3930     showAt : function(xy, parentMenu, /* private: */_e){
3931         this.parentMenu = parentMenu;
3932         if(!this.el){
3933             this.render();
3934         }
3935         if(_e !== false){
3936             this.fireEvent("beforeshow", this);
3937             //xy = this.el.adjustForConstraints(xy);
3938         }
3939         
3940         //this.el.show();
3941         this.hideMenuItems();
3942         this.hidden = false;
3943         if (this.triggerEl) {
3944             this.triggerEl.addClass('open');
3945         }
3946         
3947         this.el.addClass('show');
3948         
3949         
3950         
3951         // reassign x when hitting right
3952         
3953         // reassign y when hitting bottom
3954         
3955         // but the list may align on trigger left or trigger top... should it be a properity?
3956         
3957         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3958             this.el.setXY(xy);
3959         }
3960         
3961         this.focus();
3962         this.fireEvent("show", this);
3963     },
3964     
3965     focus : function(){
3966         return;
3967         if(!this.hidden){
3968             this.doFocus.defer(50, this);
3969         }
3970     },
3971
3972     doFocus : function(){
3973         if(!this.hidden){
3974             this.focusEl.focus();
3975         }
3976     },
3977
3978     /**
3979      * Hides this menu and optionally all parent menus
3980      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3981      */
3982     hide : function(deep)
3983     {
3984         if (false === this.fireEvent("beforehide", this)) {
3985             Roo.log("hide canceled");
3986             return;
3987         }
3988         this.hideMenuItems();
3989         if(this.el && this.isVisible()){
3990            
3991             if(this.activeItem){
3992                 this.activeItem.deactivate();
3993                 this.activeItem = null;
3994             }
3995             if (this.triggerEl) {
3996                 this.triggerEl.removeClass('open');
3997             }
3998             
3999             this.el.removeClass('show');
4000             this.hidden = true;
4001             this.fireEvent("hide", this);
4002         }
4003         if(deep === true && this.parentMenu){
4004             this.parentMenu.hide(true);
4005         }
4006     },
4007     
4008     onTriggerClick : function(e)
4009     {
4010         Roo.log('trigger click');
4011         
4012         var target = e.getTarget();
4013         
4014         Roo.log(target.nodeName.toLowerCase());
4015         
4016         if(target.nodeName.toLowerCase() === 'i'){
4017             e.preventDefault();
4018         }
4019         
4020     },
4021     
4022     onTriggerPress  : function(e)
4023     {
4024         Roo.log('trigger press');
4025         //Roo.log(e.getTarget());
4026        // Roo.log(this.triggerEl.dom);
4027        
4028         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4029         var pel = Roo.get(e.getTarget());
4030         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4031             Roo.log('is treeview or dropdown?');
4032             return;
4033         }
4034         
4035         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4036             return;
4037         }
4038         
4039         if (this.isVisible()) {
4040             Roo.log('hide');
4041             this.hide();
4042         } else {
4043             Roo.log('show');
4044             
4045             this.show(this.triggerEl, this.align, false);
4046         }
4047         
4048         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4049             e.stopEvent();
4050         }
4051         
4052     },
4053        
4054     
4055     hideMenuItems : function()
4056     {
4057         Roo.log("hide Menu Items");
4058         if (!this.el) { 
4059             return;
4060         }
4061         
4062         this.el.select('.open',true).each(function(aa) {
4063             
4064             aa.removeClass('open');
4065          
4066         });
4067     },
4068     addxtypeChild : function (tree, cntr) {
4069         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4070           
4071         this.menuitems.add(comp);
4072         return comp;
4073
4074     },
4075     getEl : function()
4076     {
4077         Roo.log(this.el);
4078         return this.el;
4079     },
4080     
4081     clear : function()
4082     {
4083         this.getEl().dom.innerHTML = '';
4084         this.menuitems.clear();
4085     }
4086 });
4087
4088  
4089  /**
4090  * @class Roo.bootstrap.menu.Item
4091  * @extends Roo.bootstrap.Component
4092  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4093  * @parent Roo.bootstrap.menu.Menu
4094  * @licence LGPL
4095  * Bootstrap MenuItem class
4096  * 
4097  * @cfg {String} html the menu label
4098  * @cfg {String} href the link
4099  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4100  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4101  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4102  * @cfg {String} fa favicon to show on left of menu item.
4103  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4104  * 
4105  * 
4106  * @constructor
4107  * Create a new MenuItem
4108  * @param {Object} config The config object
4109  */
4110
4111
4112 Roo.bootstrap.menu.Item = function(config){
4113     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4114     this.addEvents({
4115         // raw events
4116         /**
4117          * @event click
4118          * The raw click event for the entire grid.
4119          * @param {Roo.bootstrap.menu.Item} this
4120          * @param {Roo.EventObject} e
4121          */
4122         "click" : true
4123     });
4124 };
4125
4126 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4127     
4128     href : false,
4129     html : false,
4130     preventDefault: false,
4131     isContainer : false,
4132     active : false,
4133     fa: false,
4134     
4135     getAutoCreate : function(){
4136         
4137         if(this.isContainer){
4138             return {
4139                 tag: 'li',
4140                 cls: 'dropdown-menu-item '
4141             };
4142         }
4143         var ctag = {
4144             tag: 'span',
4145             html: 'Link'
4146         };
4147         
4148         var anc = {
4149             tag : 'a',
4150             cls : 'dropdown-item',
4151             href : '#',
4152             cn : [  ]
4153         };
4154         
4155         if (this.fa !== false) {
4156             anc.cn.push({
4157                 tag : 'i',
4158                 cls : 'fa fa-' + this.fa
4159             });
4160         }
4161         
4162         anc.cn.push(ctag);
4163         
4164         
4165         var cfg= {
4166             tag: 'li',
4167             cls: 'dropdown-menu-item',
4168             cn: [ anc ]
4169         };
4170         if (this.parent().type == 'treeview') {
4171             cfg.cls = 'treeview-menu';
4172         }
4173         if (this.active) {
4174             cfg.cls += ' active';
4175         }
4176         
4177         
4178         
4179         anc.href = this.href || cfg.cn[0].href ;
4180         ctag.html = this.html || cfg.cn[0].html ;
4181         return cfg;
4182     },
4183     
4184     initEvents: function()
4185     {
4186         if (this.parent().type == 'treeview') {
4187             this.el.select('a').on('click', this.onClick, this);
4188         }
4189         
4190         if (this.menu) {
4191             this.menu.parentType = this.xtype;
4192             this.menu.triggerEl = this.el;
4193             this.menu = this.addxtype(Roo.apply({}, this.menu));
4194         }
4195         
4196     },
4197     onClick : function(e)
4198     {
4199         //Roo.log('item on click ');
4200         
4201         if(this.href === false || this.preventDefault){
4202             e.preventDefault();
4203         }
4204         //this.parent().hideMenuItems();
4205         
4206         this.fireEvent('click', this, e);
4207     },
4208     getEl : function()
4209     {
4210         return this.el;
4211     } 
4212 });
4213
4214  
4215
4216  
4217
4218   
4219 /**
4220  * @class Roo.bootstrap.menu.Separator
4221  * @extends Roo.bootstrap.Component
4222  * @licence LGPL
4223  * @parent Roo.bootstrap.menu.Menu
4224  * Bootstrap Separator class
4225  * 
4226  * @constructor
4227  * Create a new Separator
4228  * @param {Object} config The config object
4229  */
4230
4231
4232 Roo.bootstrap.menu.Separator = function(config){
4233     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4234 };
4235
4236 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4237     
4238     getAutoCreate : function(){
4239         var cfg = {
4240             tag : 'li',
4241             cls: 'dropdown-divider divider'
4242         };
4243         
4244         return cfg;
4245     }
4246    
4247 });
4248
4249  
4250
4251  
4252 /*
4253 * Licence: LGPL
4254 */
4255
4256 /**
4257  * @class Roo.bootstrap.Modal
4258  * @extends Roo.bootstrap.Component
4259  * @parent none builder
4260  * @children Roo.bootstrap.Component
4261  * Bootstrap Modal class
4262  * @cfg {String} title Title of dialog
4263  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4264  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4265  * @cfg {Boolean} specificTitle default false
4266  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4267  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4268  * @cfg {Boolean} animate default true
4269  * @cfg {Boolean} allow_close default true
4270  * @cfg {Boolean} fitwindow default false
4271  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4272  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4273  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4274  * @cfg {String} size (sm|lg|xl) default empty
4275  * @cfg {Number} max_width set the max width of modal
4276  * @cfg {Boolean} editableTitle can the title be edited
4277
4278  *
4279  *
4280  * @constructor
4281  * Create a new Modal Dialog
4282  * @param {Object} config The config object
4283  */
4284
4285 Roo.bootstrap.Modal = function(config){
4286     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4287     this.addEvents({
4288         // raw events
4289         /**
4290          * @event btnclick
4291          * The raw btnclick event for the button
4292          * @param {Roo.EventObject} e
4293          */
4294         "btnclick" : true,
4295         /**
4296          * @event resize
4297          * Fire when dialog resize
4298          * @param {Roo.bootstrap.Modal} this
4299          * @param {Roo.EventObject} e
4300          */
4301         "resize" : true,
4302         /**
4303          * @event titlechanged
4304          * Fire when the editable title has been changed
4305          * @param {Roo.bootstrap.Modal} this
4306          * @param {Roo.EventObject} value
4307          */
4308         "titlechanged" : true 
4309         
4310     });
4311     this.buttons = this.buttons || [];
4312
4313     if (this.tmpl) {
4314         this.tmpl = Roo.factory(this.tmpl);
4315     }
4316
4317 };
4318
4319 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4320
4321     title : 'test dialog',
4322
4323     buttons : false,
4324
4325     // set on load...
4326
4327     html: false,
4328
4329     tmp: false,
4330
4331     specificTitle: false,
4332
4333     buttonPosition: 'right',
4334
4335     allow_close : true,
4336
4337     animate : true,
4338
4339     fitwindow: false,
4340     
4341      // private
4342     dialogEl: false,
4343     bodyEl:  false,
4344     footerEl:  false,
4345     titleEl:  false,
4346     closeEl:  false,
4347
4348     size: '',
4349     
4350     max_width: 0,
4351     
4352     max_height: 0,
4353     
4354     fit_content: false,
4355     editableTitle  : false,
4356
4357     onRender : function(ct, position)
4358     {
4359         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4360
4361         if(!this.el){
4362             var cfg = Roo.apply({},  this.getAutoCreate());
4363             cfg.id = Roo.id();
4364             //if(!cfg.name){
4365             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4366             //}
4367             //if (!cfg.name.length) {
4368             //    delete cfg.name;
4369            // }
4370             if (this.cls) {
4371                 cfg.cls += ' ' + this.cls;
4372             }
4373             if (this.style) {
4374                 cfg.style = this.style;
4375             }
4376             this.el = Roo.get(document.body).createChild(cfg, position);
4377         }
4378         //var type = this.el.dom.type;
4379
4380
4381         if(this.tabIndex !== undefined){
4382             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4383         }
4384
4385         this.dialogEl = this.el.select('.modal-dialog',true).first();
4386         this.bodyEl = this.el.select('.modal-body',true).first();
4387         this.closeEl = this.el.select('.modal-header .close', true).first();
4388         this.headerEl = this.el.select('.modal-header',true).first();
4389         this.titleEl = this.el.select('.modal-title',true).first();
4390         this.footerEl = this.el.select('.modal-footer',true).first();
4391
4392         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4393         
4394         //this.el.addClass("x-dlg-modal");
4395
4396         if (this.buttons.length) {
4397             Roo.each(this.buttons, function(bb) {
4398                 var b = Roo.apply({}, bb);
4399                 b.xns = b.xns || Roo.bootstrap;
4400                 b.xtype = b.xtype || 'Button';
4401                 if (typeof(b.listeners) == 'undefined') {
4402                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4403                 }
4404
4405                 var btn = Roo.factory(b);
4406
4407                 btn.render(this.getButtonContainer());
4408
4409             },this);
4410         }
4411         // render the children.
4412         var nitems = [];
4413
4414         if(typeof(this.items) != 'undefined'){
4415             var items = this.items;
4416             delete this.items;
4417
4418             for(var i =0;i < items.length;i++) {
4419                 // we force children not to montor widnow resize  - as we do that for them.
4420                 items[i].monitorWindowResize = false;
4421                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4422             }
4423         }
4424
4425         this.items = nitems;
4426
4427         // where are these used - they used to be body/close/footer
4428
4429
4430         this.initEvents();
4431         //this.el.addClass([this.fieldClass, this.cls]);
4432
4433     },
4434
4435     getAutoCreate : function()
4436     {
4437         // we will default to modal-body-overflow - might need to remove or make optional later.
4438         var bdy = {
4439                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4440                 html : this.html || ''
4441         };
4442
4443         var title = {
4444             tag: 'h5',
4445             cls : 'modal-title',
4446             html : this.title
4447         };
4448
4449         if(this.specificTitle){ // WTF is this?
4450             title = this.title;
4451         }
4452
4453         var header = [];
4454         if (this.allow_close && Roo.bootstrap.version == 3) {
4455             header.push({
4456                 tag: 'button',
4457                 cls : 'close',
4458                 html : '&times'
4459             });
4460         }
4461
4462         header.push(title);
4463
4464         if (this.editableTitle) {
4465             header.push({
4466                 cls: 'form-control roo-editable-title d-none',
4467                 tag: 'input',
4468                 type: 'text'
4469             });
4470         }
4471         
4472         if (this.allow_close && Roo.bootstrap.version == 4) {
4473             header.push({
4474                 tag: 'button',
4475                 cls : 'close',
4476                 html : '&times'
4477             });
4478         }
4479         
4480         var size = '';
4481
4482         if(this.size.length){
4483             size = 'modal-' + this.size;
4484         }
4485         
4486         var footer = Roo.bootstrap.version == 3 ?
4487             {
4488                 cls : 'modal-footer',
4489                 cn : [
4490                     {
4491                         tag: 'div',
4492                         cls: 'btn-' + this.buttonPosition
4493                     }
4494                 ]
4495
4496             } :
4497             {  // BS4 uses mr-auto on left buttons....
4498                 cls : 'modal-footer'
4499             };
4500
4501             
4502
4503         
4504         
4505         var modal = {
4506             cls: "modal",
4507              cn : [
4508                 {
4509                     cls: "modal-dialog " + size,
4510                     cn : [
4511                         {
4512                             cls : "modal-content",
4513                             cn : [
4514                                 {
4515                                     cls : 'modal-header',
4516                                     cn : header
4517                                 },
4518                                 bdy,
4519                                 footer
4520                             ]
4521
4522                         }
4523                     ]
4524
4525                 }
4526             ]
4527         };
4528
4529         if(this.animate){
4530             modal.cls += ' fade';
4531         }
4532
4533         return modal;
4534
4535     },
4536     getChildContainer : function() {
4537
4538          return this.bodyEl;
4539
4540     },
4541     getButtonContainer : function() {
4542         
4543          return Roo.bootstrap.version == 4 ?
4544             this.el.select('.modal-footer',true).first()
4545             : this.el.select('.modal-footer div',true).first();
4546
4547     },
4548     
4549     closeClick : function()
4550     {
4551         this.hide();
4552     },
4553     
4554     initEvents : function()
4555     {
4556         if (this.allow_close) {
4557             this.closeEl.on('click', this.closeClick, this);
4558         }
4559         Roo.EventManager.onWindowResize(this.resize, this, true);
4560         if (this.editableTitle) {
4561             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4562             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4563             this.headerEditEl.on('keyup', function(e) {
4564                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4565                         this.toggleHeaderInput(false)
4566                     }
4567                 }, this);
4568             this.headerEditEl.on('blur', function(e) {
4569                 this.toggleHeaderInput(false)
4570             },this);
4571         }
4572
4573     },
4574   
4575
4576     resize : function()
4577     {
4578         this.maskEl.setSize(
4579             Roo.lib.Dom.getViewWidth(true),
4580             Roo.lib.Dom.getViewHeight(true)
4581         );
4582         
4583         if (this.fitwindow) {
4584             
4585            this.dialogEl.setStyle( { 'max-width' : '100%' });
4586             this.setSize(
4587                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4588                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4589             );
4590             return;
4591         }
4592         
4593         if(this.max_width !== 0) {
4594             
4595             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4596             
4597             if(this.height) {
4598                 this.setSize(w, this.height);
4599                 return;
4600             }
4601             
4602             if(this.max_height) {
4603                 this.setSize(w,Math.min(
4604                     this.max_height,
4605                     Roo.lib.Dom.getViewportHeight(true) - 60
4606                 ));
4607                 
4608                 return;
4609             }
4610             
4611             if(!this.fit_content) {
4612                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4613                 return;
4614             }
4615             
4616             this.setSize(w, Math.min(
4617                 60 +
4618                 this.headerEl.getHeight() + 
4619                 this.footerEl.getHeight() + 
4620                 this.getChildHeight(this.bodyEl.dom.childNodes),
4621                 Roo.lib.Dom.getViewportHeight(true) - 60)
4622             );
4623         }
4624         
4625     },
4626
4627     setSize : function(w,h)
4628     {
4629         if (!w && !h) {
4630             return;
4631         }
4632         
4633         this.resizeTo(w,h);
4634         // any layout/border etc.. resize..
4635         (function () {
4636             this.items.forEach( function(e) {
4637                 e.layout ? e.layout() : false;
4638
4639             });
4640         }).defer(100,this);
4641         
4642     },
4643
4644     show : function() {
4645
4646         if (!this.rendered) {
4647             this.render();
4648         }
4649         this.toggleHeaderInput(false);
4650         //this.el.setStyle('display', 'block');
4651         this.el.removeClass('hideing');
4652         this.el.dom.style.display='block';
4653         
4654         Roo.get(document.body).addClass('modal-open');
4655  
4656         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4657             
4658             (function(){
4659                 this.el.addClass('show');
4660                 this.el.addClass('in');
4661             }).defer(50, this);
4662         }else{
4663             this.el.addClass('show');
4664             this.el.addClass('in');
4665         }
4666
4667         // not sure how we can show data in here..
4668         //if (this.tmpl) {
4669         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4670         //}
4671
4672         Roo.get(document.body).addClass("x-body-masked");
4673         
4674         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4675         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4676         this.maskEl.dom.style.display = 'block';
4677         this.maskEl.addClass('show');
4678         
4679         
4680         this.resize();
4681         
4682         this.fireEvent('show', this);
4683
4684         // set zindex here - otherwise it appears to be ignored...
4685         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4686         
4687         
4688         // this is for children that are... layout.Border 
4689         (function () {
4690             this.items.forEach( function(e) {
4691                 e.layout ? e.layout() : false;
4692
4693             });
4694         }).defer(100,this);
4695
4696     },
4697     hide : function()
4698     {
4699         if(this.fireEvent("beforehide", this) !== false){
4700             
4701             this.maskEl.removeClass('show');
4702             
4703             this.maskEl.dom.style.display = '';
4704             Roo.get(document.body).removeClass("x-body-masked");
4705             this.el.removeClass('in');
4706             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4707
4708             if(this.animate){ // why
4709                 this.el.addClass('hideing');
4710                 this.el.removeClass('show');
4711                 (function(){
4712                     if (!this.el.hasClass('hideing')) {
4713                         return; // it's been shown again...
4714                     }
4715                     
4716                     this.el.dom.style.display='';
4717
4718                     Roo.get(document.body).removeClass('modal-open');
4719                     this.el.removeClass('hideing');
4720                 }).defer(150,this);
4721                 
4722             }else{
4723                 this.el.removeClass('show');
4724                 this.el.dom.style.display='';
4725                 Roo.get(document.body).removeClass('modal-open');
4726
4727             }
4728             this.fireEvent('hide', this);
4729         }
4730     },
4731     isVisible : function()
4732     {
4733         
4734         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4735         
4736     },
4737
4738     addButton : function(str, cb)
4739     {
4740
4741
4742         var b = Roo.apply({}, { html : str } );
4743         b.xns = b.xns || Roo.bootstrap;
4744         b.xtype = b.xtype || 'Button';
4745         if (typeof(b.listeners) == 'undefined') {
4746             b.listeners = { click : cb.createDelegate(this)  };
4747         }
4748
4749         var btn = Roo.factory(b);
4750
4751         btn.render(this.getButtonContainer());
4752
4753         return btn;
4754
4755     },
4756
4757     setDefaultButton : function(btn)
4758     {
4759         //this.el.select('.modal-footer').()
4760     },
4761
4762     resizeTo: function(w,h)
4763     {
4764         this.dialogEl.setWidth(w);
4765         
4766         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4767
4768         this.bodyEl.setHeight(h - diff);
4769         
4770         this.fireEvent('resize', this);
4771     },
4772     
4773     setContentSize  : function(w, h)
4774     {
4775
4776     },
4777     onButtonClick: function(btn,e)
4778     {
4779         //Roo.log([a,b,c]);
4780         this.fireEvent('btnclick', btn.name, e);
4781     },
4782      /**
4783      * Set the title of the Dialog
4784      * @param {String} str new Title
4785      */
4786     setTitle: function(str) {
4787         this.titleEl.dom.innerHTML = str;
4788         this.title = str;
4789     },
4790     /**
4791      * Set the body of the Dialog
4792      * @param {String} str new Title
4793      */
4794     setBody: function(str) {
4795         this.bodyEl.dom.innerHTML = str;
4796     },
4797     /**
4798      * Set the body of the Dialog using the template
4799      * @param {Obj} data - apply this data to the template and replace the body contents.
4800      */
4801     applyBody: function(obj)
4802     {
4803         if (!this.tmpl) {
4804             Roo.log("Error - using apply Body without a template");
4805             //code
4806         }
4807         this.tmpl.overwrite(this.bodyEl, obj);
4808     },
4809     
4810     getChildHeight : function(child_nodes)
4811     {
4812         if(
4813             !child_nodes ||
4814             child_nodes.length == 0
4815         ) {
4816             return 0;
4817         }
4818         
4819         var child_height = 0;
4820         
4821         for(var i = 0; i < child_nodes.length; i++) {
4822             
4823             /*
4824             * for modal with tabs...
4825             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4826                 
4827                 var layout_childs = child_nodes[i].childNodes;
4828                 
4829                 for(var j = 0; j < layout_childs.length; j++) {
4830                     
4831                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4832                         
4833                         var layout_body_childs = layout_childs[j].childNodes;
4834                         
4835                         for(var k = 0; k < layout_body_childs.length; k++) {
4836                             
4837                             if(layout_body_childs[k].classList.contains('navbar')) {
4838                                 child_height += layout_body_childs[k].offsetHeight;
4839                                 continue;
4840                             }
4841                             
4842                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4843                                 
4844                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4845                                 
4846                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4847                                     
4848                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4849                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4850                                         continue;
4851                                     }
4852                                     
4853                                 }
4854                                 
4855                             }
4856                             
4857                         }
4858                     }
4859                 }
4860                 continue;
4861             }
4862             */
4863             
4864             child_height += child_nodes[i].offsetHeight;
4865             // Roo.log(child_nodes[i].offsetHeight);
4866         }
4867         
4868         return child_height;
4869     },
4870     toggleHeaderInput : function(is_edit)
4871     {
4872         if (!this.editableTitle) {
4873             return; // not editable.
4874         }
4875         if (is_edit && this.is_header_editing) {
4876             return; // already editing..
4877         }
4878         if (is_edit) {
4879     
4880             this.headerEditEl.dom.value = this.title;
4881             this.headerEditEl.removeClass('d-none');
4882             this.headerEditEl.dom.focus();
4883             this.titleEl.addClass('d-none');
4884             
4885             this.is_header_editing = true;
4886             return
4887         }
4888         // flip back to not editing.
4889         this.title = this.headerEditEl.dom.value;
4890         this.headerEditEl.addClass('d-none');
4891         this.titleEl.removeClass('d-none');
4892         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4893         this.is_header_editing = false;
4894         this.fireEvent('titlechanged', this, this.title);
4895     
4896             
4897         
4898     }
4899
4900 });
4901
4902
4903 Roo.apply(Roo.bootstrap.Modal,  {
4904     /**
4905          * Button config that displays a single OK button
4906          * @type Object
4907          */
4908         OK :  [{
4909             name : 'ok',
4910             weight : 'primary',
4911             html : 'OK'
4912         }],
4913         /**
4914          * Button config that displays Yes and No buttons
4915          * @type Object
4916          */
4917         YESNO : [
4918             {
4919                 name  : 'no',
4920                 html : 'No'
4921             },
4922             {
4923                 name  :'yes',
4924                 weight : 'primary',
4925                 html : 'Yes'
4926             }
4927         ],
4928
4929         /**
4930          * Button config that displays OK and Cancel buttons
4931          * @type Object
4932          */
4933         OKCANCEL : [
4934             {
4935                name : 'cancel',
4936                 html : 'Cancel'
4937             },
4938             {
4939                 name : 'ok',
4940                 weight : 'primary',
4941                 html : 'OK'
4942             }
4943         ],
4944         /**
4945          * Button config that displays Yes, No and Cancel buttons
4946          * @type Object
4947          */
4948         YESNOCANCEL : [
4949             {
4950                 name : 'yes',
4951                 weight : 'primary',
4952                 html : 'Yes'
4953             },
4954             {
4955                 name : 'no',
4956                 html : 'No'
4957             },
4958             {
4959                 name : 'cancel',
4960                 html : 'Cancel'
4961             }
4962         ],
4963         
4964         zIndex : 10001
4965 });
4966
4967 /*
4968  * - LGPL
4969  *
4970  * messagebox - can be used as a replace
4971  * 
4972  */
4973 /**
4974  * @class Roo.MessageBox
4975  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4976  * Example usage:
4977  *<pre><code>
4978 // Basic alert:
4979 Roo.Msg.alert('Status', 'Changes saved successfully.');
4980
4981 // Prompt for user data:
4982 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4983     if (btn == 'ok'){
4984         // process text value...
4985     }
4986 });
4987
4988 // Show a dialog using config options:
4989 Roo.Msg.show({
4990    title:'Save Changes?',
4991    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4992    buttons: Roo.Msg.YESNOCANCEL,
4993    fn: processResult,
4994    animEl: 'elId'
4995 });
4996 </code></pre>
4997  * @static
4998  */
4999 Roo.bootstrap.MessageBox = function(){
5000     var dlg, opt, mask, waitTimer;
5001     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5002     var buttons, activeTextEl, bwidth;
5003
5004     
5005     // private
5006     var handleButton = function(button){
5007         dlg.hide();
5008         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5009     };
5010
5011     // private
5012     var handleHide = function(){
5013         if(opt && opt.cls){
5014             dlg.el.removeClass(opt.cls);
5015         }
5016         //if(waitTimer){
5017         //    Roo.TaskMgr.stop(waitTimer);
5018         //    waitTimer = null;
5019         //}
5020     };
5021
5022     // private
5023     var updateButtons = function(b){
5024         var width = 0;
5025         if(!b){
5026             buttons["ok"].hide();
5027             buttons["cancel"].hide();
5028             buttons["yes"].hide();
5029             buttons["no"].hide();
5030             dlg.footerEl.hide();
5031             
5032             return width;
5033         }
5034         dlg.footerEl.show();
5035         for(var k in buttons){
5036             if(typeof buttons[k] != "function"){
5037                 if(b[k]){
5038                     buttons[k].show();
5039                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5040                     width += buttons[k].el.getWidth()+15;
5041                 }else{
5042                     buttons[k].hide();
5043                 }
5044             }
5045         }
5046         return width;
5047     };
5048
5049     // private
5050     var handleEsc = function(d, k, e){
5051         if(opt && opt.closable !== false){
5052             dlg.hide();
5053         }
5054         if(e){
5055             e.stopEvent();
5056         }
5057     };
5058
5059     return {
5060         /**
5061          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5062          * @return {Roo.BasicDialog} The BasicDialog element
5063          */
5064         getDialog : function(){
5065            if(!dlg){
5066                 dlg = new Roo.bootstrap.Modal( {
5067                     //draggable: true,
5068                     //resizable:false,
5069                     //constraintoviewport:false,
5070                     //fixedcenter:true,
5071                     //collapsible : false,
5072                     //shim:true,
5073                     //modal: true,
5074                 //    width: 'auto',
5075                   //  height:100,
5076                     //buttonAlign:"center",
5077                     closeClick : function(){
5078                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5079                             handleButton("no");
5080                         }else{
5081                             handleButton("cancel");
5082                         }
5083                     }
5084                 });
5085                 dlg.render();
5086                 dlg.on("hide", handleHide);
5087                 mask = dlg.mask;
5088                 //dlg.addKeyListener(27, handleEsc);
5089                 buttons = {};
5090                 this.buttons = buttons;
5091                 var bt = this.buttonText;
5092                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5093                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5094                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5095                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5096                 //Roo.log(buttons);
5097                 bodyEl = dlg.bodyEl.createChild({
5098
5099                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5100                         '<textarea class="roo-mb-textarea"></textarea>' +
5101                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5102                 });
5103                 msgEl = bodyEl.dom.firstChild;
5104                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5105                 textboxEl.enableDisplayMode();
5106                 textboxEl.addKeyListener([10,13], function(){
5107                     if(dlg.isVisible() && opt && opt.buttons){
5108                         if(opt.buttons.ok){
5109                             handleButton("ok");
5110                         }else if(opt.buttons.yes){
5111                             handleButton("yes");
5112                         }
5113                     }
5114                 });
5115                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5116                 textareaEl.enableDisplayMode();
5117                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5118                 progressEl.enableDisplayMode();
5119                 
5120                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5121                 var pf = progressEl.dom.firstChild;
5122                 if (pf) {
5123                     pp = Roo.get(pf.firstChild);
5124                     pp.setHeight(pf.offsetHeight);
5125                 }
5126                 
5127             }
5128             return dlg;
5129         },
5130
5131         /**
5132          * Updates the message box body text
5133          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5134          * the XHTML-compliant non-breaking space character '&amp;#160;')
5135          * @return {Roo.MessageBox} This message box
5136          */
5137         updateText : function(text)
5138         {
5139             if(!dlg.isVisible() && !opt.width){
5140                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5141                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5142             }
5143             msgEl.innerHTML = text || '&#160;';
5144       
5145             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5146             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5147             var w = Math.max(
5148                     Math.min(opt.width || cw , this.maxWidth), 
5149                     Math.max(opt.minWidth || this.minWidth, bwidth)
5150             );
5151             if(opt.prompt){
5152                 activeTextEl.setWidth(w);
5153             }
5154             if(dlg.isVisible()){
5155                 dlg.fixedcenter = false;
5156             }
5157             // to big, make it scroll. = But as usual stupid IE does not support
5158             // !important..
5159             
5160             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5161                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5162                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5163             } else {
5164                 bodyEl.dom.style.height = '';
5165                 bodyEl.dom.style.overflowY = '';
5166             }
5167             if (cw > w) {
5168                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5169             } else {
5170                 bodyEl.dom.style.overflowX = '';
5171             }
5172             
5173             dlg.setContentSize(w, bodyEl.getHeight());
5174             if(dlg.isVisible()){
5175                 dlg.fixedcenter = true;
5176             }
5177             return this;
5178         },
5179
5180         /**
5181          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5182          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5183          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5184          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5185          * @return {Roo.MessageBox} This message box
5186          */
5187         updateProgress : function(value, text){
5188             if(text){
5189                 this.updateText(text);
5190             }
5191             
5192             if (pp) { // weird bug on my firefox - for some reason this is not defined
5193                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5194                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5195             }
5196             return this;
5197         },        
5198
5199         /**
5200          * Returns true if the message box is currently displayed
5201          * @return {Boolean} True if the message box is visible, else false
5202          */
5203         isVisible : function(){
5204             return dlg && dlg.isVisible();  
5205         },
5206
5207         /**
5208          * Hides the message box if it is displayed
5209          */
5210         hide : function(){
5211             if(this.isVisible()){
5212                 dlg.hide();
5213             }  
5214         },
5215
5216         /**
5217          * Displays a new message box, or reinitializes an existing message box, based on the config options
5218          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5219          * The following config object properties are supported:
5220          * <pre>
5221 Property    Type             Description
5222 ----------  ---------------  ------------------------------------------------------------------------------------
5223 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5224                                    closes (defaults to undefined)
5225 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5226                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5227 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5228                                    progress and wait dialogs will ignore this property and always hide the
5229                                    close button as they can only be closed programmatically.
5230 cls               String           A custom CSS class to apply to the message box element
5231 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5232                                    displayed (defaults to 75)
5233 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5234                                    function will be btn (the name of the button that was clicked, if applicable,
5235                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5236                                    Progress and wait dialogs will ignore this option since they do not respond to
5237                                    user actions and can only be closed programmatically, so any required function
5238                                    should be called by the same code after it closes the dialog.
5239 icon              String           A CSS class that provides a background image to be used as an icon for
5240                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5241 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5242 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5243 modal             Boolean          False to allow user interaction with the page while the message box is
5244                                    displayed (defaults to true)
5245 msg               String           A string that will replace the existing message box body text (defaults
5246                                    to the XHTML-compliant non-breaking space character '&#160;')
5247 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5248 progress          Boolean          True to display a progress bar (defaults to false)
5249 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5250 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5251 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5252 title             String           The title text
5253 value             String           The string value to set into the active textbox element if displayed
5254 wait              Boolean          True to display a progress bar (defaults to false)
5255 width             Number           The width of the dialog in pixels
5256 </pre>
5257          *
5258          * Example usage:
5259          * <pre><code>
5260 Roo.Msg.show({
5261    title: 'Address',
5262    msg: 'Please enter your address:',
5263    width: 300,
5264    buttons: Roo.MessageBox.OKCANCEL,
5265    multiline: true,
5266    fn: saveAddress,
5267    animEl: 'addAddressBtn'
5268 });
5269 </code></pre>
5270          * @param {Object} config Configuration options
5271          * @return {Roo.MessageBox} This message box
5272          */
5273         show : function(options)
5274         {
5275             
5276             // this causes nightmares if you show one dialog after another
5277             // especially on callbacks..
5278              
5279             if(this.isVisible()){
5280                 
5281                 this.hide();
5282                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5283                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5284                 Roo.log("New Dialog Message:" +  options.msg )
5285                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5286                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5287                 
5288             }
5289             var d = this.getDialog();
5290             opt = options;
5291             d.setTitle(opt.title || "&#160;");
5292             d.closeEl.setDisplayed(opt.closable !== false);
5293             activeTextEl = textboxEl;
5294             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5295             if(opt.prompt){
5296                 if(opt.multiline){
5297                     textboxEl.hide();
5298                     textareaEl.show();
5299                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5300                         opt.multiline : this.defaultTextHeight);
5301                     activeTextEl = textareaEl;
5302                 }else{
5303                     textboxEl.show();
5304                     textareaEl.hide();
5305                 }
5306             }else{
5307                 textboxEl.hide();
5308                 textareaEl.hide();
5309             }
5310             progressEl.setDisplayed(opt.progress === true);
5311             if (opt.progress) {
5312                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5313             }
5314             this.updateProgress(0);
5315             activeTextEl.dom.value = opt.value || "";
5316             if(opt.prompt){
5317                 dlg.setDefaultButton(activeTextEl);
5318             }else{
5319                 var bs = opt.buttons;
5320                 var db = null;
5321                 if(bs && bs.ok){
5322                     db = buttons["ok"];
5323                 }else if(bs && bs.yes){
5324                     db = buttons["yes"];
5325                 }
5326                 dlg.setDefaultButton(db);
5327             }
5328             bwidth = updateButtons(opt.buttons);
5329             this.updateText(opt.msg);
5330             if(opt.cls){
5331                 d.el.addClass(opt.cls);
5332             }
5333             d.proxyDrag = opt.proxyDrag === true;
5334             d.modal = opt.modal !== false;
5335             d.mask = opt.modal !== false ? mask : false;
5336             if(!d.isVisible()){
5337                 // force it to the end of the z-index stack so it gets a cursor in FF
5338                 document.body.appendChild(dlg.el.dom);
5339                 d.animateTarget = null;
5340                 d.show(options.animEl);
5341             }
5342             return this;
5343         },
5344
5345         /**
5346          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5347          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5348          * and closing the message box when the process is complete.
5349          * @param {String} title The title bar text
5350          * @param {String} msg The message box body text
5351          * @return {Roo.MessageBox} This message box
5352          */
5353         progress : function(title, msg){
5354             this.show({
5355                 title : title,
5356                 msg : msg,
5357                 buttons: false,
5358                 progress:true,
5359                 closable:false,
5360                 minWidth: this.minProgressWidth,
5361                 modal : true
5362             });
5363             return this;
5364         },
5365
5366         /**
5367          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5368          * If a callback function is passed it will be called after the user clicks the button, and the
5369          * id of the button that was clicked will be passed as the only parameter to the callback
5370          * (could also be the top-right close button).
5371          * @param {String} title The title bar text
5372          * @param {String} msg The message box body text
5373          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5374          * @param {Object} scope (optional) The scope of the callback function
5375          * @return {Roo.MessageBox} This message box
5376          */
5377         alert : function(title, msg, fn, scope)
5378         {
5379             this.show({
5380                 title : title,
5381                 msg : msg,
5382                 buttons: this.OK,
5383                 fn: fn,
5384                 closable : false,
5385                 scope : scope,
5386                 modal : true
5387             });
5388             return this;
5389         },
5390
5391         /**
5392          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5393          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5394          * You are responsible for closing the message box when the process is complete.
5395          * @param {String} msg The message box body text
5396          * @param {String} title (optional) The title bar text
5397          * @return {Roo.MessageBox} This message box
5398          */
5399         wait : function(msg, title){
5400             this.show({
5401                 title : title,
5402                 msg : msg,
5403                 buttons: false,
5404                 closable:false,
5405                 progress:true,
5406                 modal:true,
5407                 width:300,
5408                 wait:true
5409             });
5410             waitTimer = Roo.TaskMgr.start({
5411                 run: function(i){
5412                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5413                 },
5414                 interval: 1000
5415             });
5416             return this;
5417         },
5418
5419         /**
5420          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5421          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5422          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5423          * @param {String} title The title bar text
5424          * @param {String} msg The message box body text
5425          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426          * @param {Object} scope (optional) The scope of the callback function
5427          * @return {Roo.MessageBox} This message box
5428          */
5429         confirm : function(title, msg, fn, scope){
5430             this.show({
5431                 title : title,
5432                 msg : msg,
5433                 buttons: this.YESNO,
5434                 fn: fn,
5435                 scope : scope,
5436                 modal : true
5437             });
5438             return this;
5439         },
5440
5441         /**
5442          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5443          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5444          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5445          * (could also be the top-right close button) and the text that was entered will be passed as the two
5446          * parameters to the callback.
5447          * @param {String} title The title bar text
5448          * @param {String} msg The message box body text
5449          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5450          * @param {Object} scope (optional) The scope of the callback function
5451          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5452          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5453          * @return {Roo.MessageBox} This message box
5454          */
5455         prompt : function(title, msg, fn, scope, multiline){
5456             this.show({
5457                 title : title,
5458                 msg : msg,
5459                 buttons: this.OKCANCEL,
5460                 fn: fn,
5461                 minWidth:250,
5462                 scope : scope,
5463                 prompt:true,
5464                 multiline: multiline,
5465                 modal : true
5466             });
5467             return this;
5468         },
5469
5470         /**
5471          * Button config that displays a single OK button
5472          * @type Object
5473          */
5474         OK : {ok:true},
5475         /**
5476          * Button config that displays Yes and No buttons
5477          * @type Object
5478          */
5479         YESNO : {yes:true, no:true},
5480         /**
5481          * Button config that displays OK and Cancel buttons
5482          * @type Object
5483          */
5484         OKCANCEL : {ok:true, cancel:true},
5485         /**
5486          * Button config that displays Yes, No and Cancel buttons
5487          * @type Object
5488          */
5489         YESNOCANCEL : {yes:true, no:true, cancel:true},
5490
5491         /**
5492          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5493          * @type Number
5494          */
5495         defaultTextHeight : 75,
5496         /**
5497          * The maximum width in pixels of the message box (defaults to 600)
5498          * @type Number
5499          */
5500         maxWidth : 600,
5501         /**
5502          * The minimum width in pixels of the message box (defaults to 100)
5503          * @type Number
5504          */
5505         minWidth : 100,
5506         /**
5507          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5508          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5509          * @type Number
5510          */
5511         minProgressWidth : 250,
5512         /**
5513          * An object containing the default button text strings that can be overriden for localized language support.
5514          * Supported properties are: ok, cancel, yes and no.
5515          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5516          * @type Object
5517          */
5518         buttonText : {
5519             ok : "OK",
5520             cancel : "Cancel",
5521             yes : "Yes",
5522             no : "No"
5523         }
5524     };
5525 }();
5526
5527 /**
5528  * Shorthand for {@link Roo.MessageBox}
5529  */
5530 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5531 Roo.Msg = Roo.Msg || Roo.MessageBox;
5532 /*
5533  * - LGPL
5534  *
5535  * navbar
5536  * 
5537  */
5538
5539 /**
5540  * @class Roo.bootstrap.nav.Bar
5541  * @extends Roo.bootstrap.Component
5542  * @abstract
5543  * Bootstrap Navbar class
5544
5545  * @constructor
5546  * Create a new Navbar
5547  * @param {Object} config The config object
5548  */
5549
5550
5551 Roo.bootstrap.nav.Bar = function(config){
5552     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5553     this.addEvents({
5554         // raw events
5555         /**
5556          * @event beforetoggle
5557          * Fire before toggle the menu
5558          * @param {Roo.EventObject} e
5559          */
5560         "beforetoggle" : true
5561     });
5562 };
5563
5564 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5565     
5566     
5567    
5568     // private
5569     navItems : false,
5570     loadMask : false,
5571     
5572     
5573     getAutoCreate : function(){
5574         
5575         
5576         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5577         
5578     },
5579     
5580     initEvents :function ()
5581     {
5582         //Roo.log(this.el.select('.navbar-toggle',true));
5583         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5584         
5585         var mark = {
5586             tag: "div",
5587             cls:"x-dlg-mask"
5588         };
5589         
5590         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5591         
5592         var size = this.el.getSize();
5593         this.maskEl.setSize(size.width, size.height);
5594         this.maskEl.enableDisplayMode("block");
5595         this.maskEl.hide();
5596         
5597         if(this.loadMask){
5598             this.maskEl.show();
5599         }
5600     },
5601     
5602     
5603     getChildContainer : function()
5604     {
5605         if (this.el && this.el.select('.collapse').getCount()) {
5606             return this.el.select('.collapse',true).first();
5607         }
5608         
5609         return this.el;
5610     },
5611     
5612     mask : function()
5613     {
5614         this.maskEl.show();
5615     },
5616     
5617     unmask : function()
5618     {
5619         this.maskEl.hide();
5620     },
5621     onToggle : function()
5622     {
5623         
5624         if(this.fireEvent('beforetoggle', this) === false){
5625             return;
5626         }
5627         var ce = this.el.select('.navbar-collapse',true).first();
5628       
5629         if (!ce.hasClass('show')) {
5630            this.expand();
5631         } else {
5632             this.collapse();
5633         }
5634         
5635         
5636     
5637     },
5638     /**
5639      * Expand the navbar pulldown 
5640      */
5641     expand : function ()
5642     {
5643        
5644         var ce = this.el.select('.navbar-collapse',true).first();
5645         if (ce.hasClass('collapsing')) {
5646             return;
5647         }
5648         ce.dom.style.height = '';
5649                // show it...
5650         ce.addClass('in'); // old...
5651         ce.removeClass('collapse');
5652         ce.addClass('show');
5653         var h = ce.getHeight();
5654         Roo.log(h);
5655         ce.removeClass('show');
5656         // at this point we should be able to see it..
5657         ce.addClass('collapsing');
5658         
5659         ce.setHeight(0); // resize it ...
5660         ce.on('transitionend', function() {
5661             //Roo.log('done transition');
5662             ce.removeClass('collapsing');
5663             ce.addClass('show');
5664             ce.removeClass('collapse');
5665
5666             ce.dom.style.height = '';
5667         }, this, { single: true} );
5668         ce.setHeight(h);
5669         ce.dom.scrollTop = 0;
5670     },
5671     /**
5672      * Collapse the navbar pulldown 
5673      */
5674     collapse : function()
5675     {
5676          var ce = this.el.select('.navbar-collapse',true).first();
5677        
5678         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5679             // it's collapsed or collapsing..
5680             return;
5681         }
5682         ce.removeClass('in'); // old...
5683         ce.setHeight(ce.getHeight());
5684         ce.removeClass('show');
5685         ce.addClass('collapsing');
5686         
5687         ce.on('transitionend', function() {
5688             ce.dom.style.height = '';
5689             ce.removeClass('collapsing');
5690             ce.addClass('collapse');
5691         }, this, { single: true} );
5692         ce.setHeight(0);
5693     }
5694     
5695     
5696     
5697 });
5698
5699
5700
5701  
5702
5703  /*
5704  * - LGPL
5705  *
5706  * navbar
5707  * 
5708  */
5709
5710 /**
5711  * @class Roo.bootstrap.nav.Simplebar
5712  * @extends Roo.bootstrap.nav.Bar
5713  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5714  * Bootstrap Sidebar class
5715  *
5716  * @cfg {Boolean} inverse is inverted color
5717  * 
5718  * @cfg {String} type (nav | pills | tabs)
5719  * @cfg {Boolean} arrangement stacked | justified
5720  * @cfg {String} align (left | right) alignment
5721  * 
5722  * @cfg {Boolean} main (true|false) main nav bar? default false
5723  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5724  * 
5725  * @cfg {String} tag (header|footer|nav|div) default is nav 
5726
5727  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5728  * 
5729  * 
5730  * @constructor
5731  * Create a new Sidebar
5732  * @param {Object} config The config object
5733  */
5734
5735
5736 Roo.bootstrap.nav.Simplebar = function(config){
5737     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5738 };
5739
5740 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5741     
5742     inverse: false,
5743     
5744     type: false,
5745     arrangement: '',
5746     align : false,
5747     
5748     weight : 'light',
5749     
5750     main : false,
5751     
5752     
5753     tag : false,
5754     
5755     
5756     getAutoCreate : function(){
5757         
5758         
5759         var cfg = {
5760             tag : this.tag || 'div',
5761             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5762         };
5763         if (['light','white'].indexOf(this.weight) > -1) {
5764             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5765         }
5766         cfg.cls += ' bg-' + this.weight;
5767         
5768         if (this.inverse) {
5769             cfg.cls += ' navbar-inverse';
5770             
5771         }
5772         
5773         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5774         
5775         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5776             return cfg;
5777         }
5778         
5779         
5780     
5781         
5782         cfg.cn = [
5783             {
5784                 cls: 'nav nav-' + this.xtype,
5785                 tag : 'ul'
5786             }
5787         ];
5788         
5789          
5790         this.type = this.type || 'nav';
5791         if (['tabs','pills'].indexOf(this.type) != -1) {
5792             cfg.cn[0].cls += ' nav-' + this.type
5793         
5794         
5795         } else {
5796             if (this.type!=='nav') {
5797                 Roo.log('nav type must be nav/tabs/pills')
5798             }
5799             cfg.cn[0].cls += ' navbar-nav'
5800         }
5801         
5802         
5803         
5804         
5805         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5806             cfg.cn[0].cls += ' nav-' + this.arrangement;
5807         }
5808         
5809         
5810         if (this.align === 'right') {
5811             cfg.cn[0].cls += ' navbar-right';
5812         }
5813         
5814         
5815         
5816         
5817         return cfg;
5818     
5819         
5820     }
5821     
5822     
5823     
5824 });
5825
5826
5827
5828  
5829
5830  
5831        /*
5832  * - LGPL
5833  *
5834  * navbar
5835  * navbar-fixed-top
5836  * navbar-expand-md  fixed-top 
5837  */
5838
5839 /**
5840  * @class Roo.bootstrap.nav.Headerbar
5841  * @extends Roo.bootstrap.nav.Simplebar
5842  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5843  * Bootstrap Sidebar class
5844  *
5845  * @cfg {String} brand what is brand
5846  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5847  * @cfg {String} brand_href href of the brand
5848  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5849  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5850  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5851  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5852  * 
5853  * @constructor
5854  * Create a new Sidebar
5855  * @param {Object} config The config object
5856  */
5857
5858
5859 Roo.bootstrap.nav.Headerbar = function(config){
5860     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5861       
5862 };
5863
5864 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5865     
5866     position: '',
5867     brand: '',
5868     brand_href: false,
5869     srButton : true,
5870     autohide : false,
5871     desktopCenter : false,
5872    
5873     
5874     getAutoCreate : function(){
5875         
5876         var   cfg = {
5877             tag: this.nav || 'nav',
5878             cls: 'navbar navbar-expand-md',
5879             role: 'navigation',
5880             cn: []
5881         };
5882         
5883         var cn = cfg.cn;
5884         if (this.desktopCenter) {
5885             cn.push({cls : 'container', cn : []});
5886             cn = cn[0].cn;
5887         }
5888         
5889         if(this.srButton){
5890             var btn = {
5891                 tag: 'button',
5892                 type: 'button',
5893                 cls: 'navbar-toggle navbar-toggler',
5894                 'data-toggle': 'collapse',
5895                 cn: [
5896                     {
5897                         tag: 'span',
5898                         cls: 'sr-only',
5899                         html: 'Toggle navigation'
5900                     },
5901                     {
5902                         tag: 'span',
5903                         cls: 'icon-bar navbar-toggler-icon'
5904                     },
5905                     {
5906                         tag: 'span',
5907                         cls: 'icon-bar'
5908                     },
5909                     {
5910                         tag: 'span',
5911                         cls: 'icon-bar'
5912                     }
5913                 ]
5914             };
5915             
5916             cn.push( Roo.bootstrap.version == 4 ? btn : {
5917                 tag: 'div',
5918                 cls: 'navbar-header',
5919                 cn: [
5920                     btn
5921                 ]
5922             });
5923         }
5924         
5925         cn.push({
5926             tag: 'div',
5927             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5928             cn : []
5929         });
5930         
5931         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5932         
5933         if (['light','white'].indexOf(this.weight) > -1) {
5934             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5935         }
5936         cfg.cls += ' bg-' + this.weight;
5937         
5938         
5939         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5940             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5941             
5942             // tag can override this..
5943             
5944             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5945         }
5946         
5947         if (this.brand !== '') {
5948             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5949             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5950                 tag: 'a',
5951                 href: this.brand_href ? this.brand_href : '#',
5952                 cls: 'navbar-brand',
5953                 cn: [
5954                 this.brand
5955                 ]
5956             });
5957         }
5958         
5959         if(this.main){
5960             cfg.cls += ' main-nav';
5961         }
5962         
5963         
5964         return cfg;
5965
5966         
5967     },
5968     getHeaderChildContainer : function()
5969     {
5970         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5971             return this.el.select('.navbar-header',true).first();
5972         }
5973         
5974         return this.getChildContainer();
5975     },
5976     
5977     getChildContainer : function()
5978     {
5979          
5980         return this.el.select('.roo-navbar-collapse',true).first();
5981          
5982         
5983     },
5984     
5985     initEvents : function()
5986     {
5987         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5988         
5989         if (this.autohide) {
5990             
5991             var prevScroll = 0;
5992             var ft = this.el;
5993             
5994             Roo.get(document).on('scroll',function(e) {
5995                 var ns = Roo.get(document).getScroll().top;
5996                 var os = prevScroll;
5997                 prevScroll = ns;
5998                 
5999                 if(ns > os){
6000                     ft.removeClass('slideDown');
6001                     ft.addClass('slideUp');
6002                     return;
6003                 }
6004                 ft.removeClass('slideUp');
6005                 ft.addClass('slideDown');
6006                  
6007               
6008           },this);
6009         }
6010     }    
6011     
6012 });
6013
6014
6015
6016  
6017
6018  /*
6019  * - LGPL
6020  *
6021  * navbar
6022  * 
6023  */
6024
6025 /**
6026  * @class Roo.bootstrap.nav.Sidebar
6027  * @extends Roo.bootstrap.nav.Bar
6028  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6029  * Bootstrap Sidebar class
6030  * 
6031  * @constructor
6032  * Create a new Sidebar
6033  * @param {Object} config The config object
6034  */
6035
6036
6037 Roo.bootstrap.nav.Sidebar = function(config){
6038     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6039 };
6040
6041 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6042     
6043     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6044     
6045     getAutoCreate : function(){
6046         
6047         
6048         return  {
6049             tag: 'div',
6050             cls: 'sidebar sidebar-nav'
6051         };
6052     
6053         
6054     }
6055     
6056     
6057     
6058 });
6059
6060
6061
6062  
6063
6064  /*
6065  * - LGPL
6066  *
6067  * nav group
6068  * 
6069  */
6070
6071 /**
6072  * @class Roo.bootstrap.nav.Group
6073  * @extends Roo.bootstrap.Component
6074  * @children Roo.bootstrap.nav.Item
6075  * Bootstrap NavGroup class
6076  * @cfg {String} align (left|right)
6077  * @cfg {Boolean} inverse
6078  * @cfg {String} type (nav|pills|tab) default nav
6079  * @cfg {String} navId - reference Id for navbar.
6080  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6081  * 
6082  * @constructor
6083  * Create a new nav group
6084  * @param {Object} config The config object
6085  */
6086
6087 Roo.bootstrap.nav.Group = function(config){
6088     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6089     this.navItems = [];
6090    
6091     Roo.bootstrap.nav.Group.register(this);
6092      this.addEvents({
6093         /**
6094              * @event changed
6095              * Fires when the active item changes
6096              * @param {Roo.bootstrap.nav.Group} this
6097              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6098              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6099          */
6100         'changed': true
6101      });
6102     
6103 };
6104
6105 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6106     
6107     align: '',
6108     inverse: false,
6109     form: false,
6110     type: 'nav',
6111     navId : '',
6112     // private
6113     pilltype : true,
6114     
6115     navItems : false, 
6116     
6117     getAutoCreate : function()
6118     {
6119         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6120         
6121         cfg = {
6122             tag : 'ul',
6123             cls: 'nav' 
6124         };
6125         if (Roo.bootstrap.version == 4) {
6126             if (['tabs','pills'].indexOf(this.type) != -1) {
6127                 cfg.cls += ' nav-' + this.type; 
6128             } else {
6129                 // trying to remove so header bar can right align top?
6130                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6131                     // do not use on header bar... 
6132                     cfg.cls += ' navbar-nav';
6133                 }
6134             }
6135             
6136         } else {
6137             if (['tabs','pills'].indexOf(this.type) != -1) {
6138                 cfg.cls += ' nav-' + this.type
6139             } else {
6140                 if (this.type !== 'nav') {
6141                     Roo.log('nav type must be nav/tabs/pills')
6142                 }
6143                 cfg.cls += ' navbar-nav'
6144             }
6145         }
6146         
6147         if (this.parent() && this.parent().sidebar) {
6148             cfg = {
6149                 tag: 'ul',
6150                 cls: 'dashboard-menu sidebar-menu'
6151             };
6152             
6153             return cfg;
6154         }
6155         
6156         if (this.form === true) {
6157             cfg = {
6158                 tag: 'form',
6159                 cls: 'navbar-form form-inline'
6160             };
6161             //nav navbar-right ml-md-auto
6162             if (this.align === 'right') {
6163                 cfg.cls += ' navbar-right ml-md-auto';
6164             } else {
6165                 cfg.cls += ' navbar-left';
6166             }
6167         }
6168         
6169         if (this.align === 'right') {
6170             cfg.cls += ' navbar-right ml-md-auto';
6171         } else {
6172             cfg.cls += ' mr-auto';
6173         }
6174         
6175         if (this.inverse) {
6176             cfg.cls += ' navbar-inverse';
6177             
6178         }
6179         
6180         
6181         return cfg;
6182     },
6183     /**
6184     * sets the active Navigation item
6185     * @param {Roo.bootstrap.nav.Item} the new current navitem
6186     */
6187     setActiveItem : function(item)
6188     {
6189         var prev = false;
6190         Roo.each(this.navItems, function(v){
6191             if (v == item) {
6192                 return ;
6193             }
6194             if (v.isActive()) {
6195                 v.setActive(false, true);
6196                 prev = v;
6197                 
6198             }
6199             
6200         });
6201
6202         item.setActive(true, true);
6203         this.fireEvent('changed', this, item, prev);
6204         
6205         
6206     },
6207     /**
6208     * gets the active Navigation item
6209     * @return {Roo.bootstrap.nav.Item} the current navitem
6210     */
6211     getActive : function()
6212     {
6213         
6214         var prev = false;
6215         Roo.each(this.navItems, function(v){
6216             
6217             if (v.isActive()) {
6218                 prev = v;
6219                 
6220             }
6221             
6222         });
6223         return prev;
6224     },
6225     
6226     indexOfNav : function()
6227     {
6228         
6229         var prev = false;
6230         Roo.each(this.navItems, function(v,i){
6231             
6232             if (v.isActive()) {
6233                 prev = i;
6234                 
6235             }
6236             
6237         });
6238         return prev;
6239     },
6240     /**
6241     * adds a Navigation item
6242     * @param {Roo.bootstrap.nav.Item} the navitem to add
6243     */
6244     addItem : function(cfg)
6245     {
6246         if (this.form && Roo.bootstrap.version == 4) {
6247             cfg.tag = 'div';
6248         }
6249         var cn = new Roo.bootstrap.nav.Item(cfg);
6250         this.register(cn);
6251         cn.parentId = this.id;
6252         cn.onRender(this.el, null);
6253         return cn;
6254     },
6255     /**
6256     * register a Navigation item
6257     * @param {Roo.bootstrap.nav.Item} the navitem to add
6258     */
6259     register : function(item)
6260     {
6261         this.navItems.push( item);
6262         item.navId = this.navId;
6263     
6264     },
6265     
6266     /**
6267     * clear all the Navigation item
6268     */
6269    
6270     clearAll : function()
6271     {
6272         this.navItems = [];
6273         this.el.dom.innerHTML = '';
6274     },
6275     
6276     getNavItem: function(tabId)
6277     {
6278         var ret = false;
6279         Roo.each(this.navItems, function(e) {
6280             if (e.tabId == tabId) {
6281                ret =  e;
6282                return false;
6283             }
6284             return true;
6285             
6286         });
6287         return ret;
6288     },
6289     
6290     setActiveNext : function()
6291     {
6292         var i = this.indexOfNav(this.getActive());
6293         if (i > this.navItems.length) {
6294             return;
6295         }
6296         this.setActiveItem(this.navItems[i+1]);
6297     },
6298     setActivePrev : function()
6299     {
6300         var i = this.indexOfNav(this.getActive());
6301         if (i  < 1) {
6302             return;
6303         }
6304         this.setActiveItem(this.navItems[i-1]);
6305     },
6306     clearWasActive : function(except) {
6307         Roo.each(this.navItems, function(e) {
6308             if (e.tabId != except.tabId && e.was_active) {
6309                e.was_active = false;
6310                return false;
6311             }
6312             return true;
6313             
6314         });
6315     },
6316     getWasActive : function ()
6317     {
6318         var r = false;
6319         Roo.each(this.navItems, function(e) {
6320             if (e.was_active) {
6321                r = e;
6322                return false;
6323             }
6324             return true;
6325             
6326         });
6327         return r;
6328     }
6329     
6330     
6331 });
6332
6333  
6334 Roo.apply(Roo.bootstrap.nav.Group, {
6335     
6336     groups: {},
6337      /**
6338     * register a Navigation Group
6339     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6340     */
6341     register : function(navgrp)
6342     {
6343         this.groups[navgrp.navId] = navgrp;
6344         
6345     },
6346     /**
6347     * fetch a Navigation Group based on the navigation ID
6348     * @param {string} the navgroup to add
6349     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6350     */
6351     get: function(navId) {
6352         if (typeof(this.groups[navId]) == 'undefined') {
6353             return false;
6354             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6355         }
6356         return this.groups[navId] ;
6357     }
6358     
6359     
6360     
6361 });
6362
6363  /**
6364  * @class Roo.bootstrap.nav.Item
6365  * @extends Roo.bootstrap.Component
6366  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6367  * @parent Roo.bootstrap.nav.Group
6368  * @licence LGPL
6369  * Bootstrap Navbar.NavItem class
6370  * 
6371  * @cfg {String} href  link to
6372  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6373  * @cfg {Boolean} button_outline show and outlined button
6374  * @cfg {String} html content of button
6375  * @cfg {String} badge text inside badge
6376  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6377  * @cfg {String} glyphicon DEPRICATED - use fa
6378  * @cfg {String} icon DEPRICATED - use fa
6379  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6380  * @cfg {Boolean} active Is item active
6381  * @cfg {Boolean} disabled Is item disabled
6382  * @cfg {String} linkcls  Link Class
6383  * @cfg {Boolean} preventDefault (true | false) default false
6384  * @cfg {String} tabId the tab that this item activates.
6385  * @cfg {String} tagtype (a|span) render as a href or span?
6386  * @cfg {Boolean} animateRef (true|false) link to element default false  
6387  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6388   
6389  * @constructor
6390  * Create a new Navbar Item
6391  * @param {Object} config The config object
6392  */
6393 Roo.bootstrap.nav.Item = function(config){
6394     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6395     this.addEvents({
6396         // raw events
6397         /**
6398          * @event click
6399          * The raw click event for the entire grid.
6400          * @param {Roo.EventObject} e
6401          */
6402         "click" : true,
6403          /**
6404             * @event changed
6405             * Fires when the active item active state changes
6406             * @param {Roo.bootstrap.nav.Item} this
6407             * @param {boolean} state the new state
6408              
6409          */
6410         'changed': true,
6411         /**
6412             * @event scrollto
6413             * Fires when scroll to element
6414             * @param {Roo.bootstrap.nav.Item} this
6415             * @param {Object} options
6416             * @param {Roo.EventObject} e
6417              
6418          */
6419         'scrollto': true
6420     });
6421    
6422 };
6423
6424 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6425     
6426     href: false,
6427     html: '',
6428     badge: '',
6429     icon: false,
6430     fa : false,
6431     glyphicon: false,
6432     active: false,
6433     preventDefault : false,
6434     tabId : false,
6435     tagtype : 'a',
6436     tag: 'li',
6437     disabled : false,
6438     animateRef : false,
6439     was_active : false,
6440     button_weight : '',
6441     button_outline : false,
6442     linkcls : '',
6443     navLink: false,
6444     
6445     getAutoCreate : function(){
6446          
6447         var cfg = {
6448             tag: this.tag,
6449             cls: 'nav-item'
6450         };
6451         
6452         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6453         
6454         if (this.active) {
6455             cfg.cls +=  ' active' ;
6456         }
6457         if (this.disabled) {
6458             cfg.cls += ' disabled';
6459         }
6460         
6461         // BS4 only?
6462         if (this.button_weight.length) {
6463             cfg.tag = this.href ? 'a' : 'button';
6464             cfg.html = this.html || '';
6465             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6466             if (this.href) {
6467                 cfg.href = this.href;
6468             }
6469             if (this.fa) {
6470                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6471             } else {
6472                 cfg.cls += " nav-html";
6473             }
6474             
6475             // menu .. should add dropdown-menu class - so no need for carat..
6476             
6477             if (this.badge !== '') {
6478                  
6479                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6480             }
6481             return cfg;
6482         }
6483         
6484         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6485             cfg.cn = [
6486                 {
6487                     tag: this.tagtype,
6488                     href : this.href || "#",
6489                     html: this.html || '',
6490                     cls : ''
6491                 }
6492             ];
6493             if (this.tagtype == 'a') {
6494                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6495         
6496             }
6497             if (this.icon) {
6498                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6499             } else  if (this.fa) {
6500                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6501             } else if(this.glyphicon) {
6502                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6503             } else {
6504                 cfg.cn[0].cls += " nav-html";
6505             }
6506             
6507             if (this.menu) {
6508                 cfg.cn[0].html += " <span class='caret'></span>";
6509              
6510             }
6511             
6512             if (this.badge !== '') {
6513                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6514             }
6515         }
6516         
6517         
6518         
6519         return cfg;
6520     },
6521     onRender : function(ct, position)
6522     {
6523        // Roo.log("Call onRender: " + this.xtype);
6524         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6525             this.tag = 'div';
6526         }
6527         
6528         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6529         this.navLink = this.el.select('.nav-link',true).first();
6530         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6531         return ret;
6532     },
6533       
6534     
6535     initEvents: function() 
6536     {
6537         if (typeof (this.menu) != 'undefined') {
6538             this.menu.parentType = this.xtype;
6539             this.menu.triggerEl = this.el;
6540             this.menu = this.addxtype(Roo.apply({}, this.menu));
6541         }
6542         
6543         this.el.on('click', this.onClick, this);
6544         
6545         //if(this.tagtype == 'span'){
6546         //    this.el.select('span',true).on('click', this.onClick, this);
6547         //}
6548        
6549         // at this point parent should be available..
6550         this.parent().register(this);
6551     },
6552     
6553     onClick : function(e)
6554     {
6555         if (e.getTarget('.dropdown-menu-item')) {
6556             // did you click on a menu itemm.... - then don't trigger onclick..
6557             return;
6558         }
6559         
6560         if(
6561                 this.preventDefault ||
6562                                 this.href === false ||
6563                 this.href === '#' 
6564         ){
6565             //Roo.log("NavItem - prevent Default?");
6566             e.preventDefault();
6567         }
6568         
6569         if (this.disabled) {
6570             return;
6571         }
6572         
6573         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6574         if (tg && tg.transition) {
6575             Roo.log("waiting for the transitionend");
6576             return;
6577         }
6578         
6579         
6580         
6581         //Roo.log("fire event clicked");
6582         if(this.fireEvent('click', this, e) === false){
6583             return;
6584         };
6585         
6586         if(this.tagtype == 'span'){
6587             return;
6588         }
6589         
6590         //Roo.log(this.href);
6591         var ael = this.el.select('a',true).first();
6592         //Roo.log(ael);
6593         
6594         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6595             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6596             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6597                 return; // ignore... - it's a 'hash' to another page.
6598             }
6599             Roo.log("NavItem - prevent Default?");
6600             e.preventDefault();
6601             this.scrollToElement(e);
6602         }
6603         
6604         
6605         var p =  this.parent();
6606    
6607         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6608             if (typeof(p.setActiveItem) !== 'undefined') {
6609                 p.setActiveItem(this);
6610             }
6611         }
6612         
6613         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6614         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6615             // remove the collapsed menu expand...
6616             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6617         }
6618     },
6619     
6620     isActive: function () {
6621         return this.active
6622     },
6623     setActive : function(state, fire, is_was_active)
6624     {
6625         if (this.active && !state && this.navId) {
6626             this.was_active = true;
6627             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6628             if (nv) {
6629                 nv.clearWasActive(this);
6630             }
6631             
6632         }
6633         this.active = state;
6634         
6635         if (!state ) {
6636             this.el.removeClass('active');
6637             this.navLink ? this.navLink.removeClass('active') : false;
6638         } else if (!this.el.hasClass('active')) {
6639             
6640             this.el.addClass('active');
6641             if (Roo.bootstrap.version == 4 && this.navLink ) {
6642                 this.navLink.addClass('active');
6643             }
6644             
6645         }
6646         if (fire) {
6647             this.fireEvent('changed', this, state);
6648         }
6649         
6650         // show a panel if it's registered and related..
6651         
6652         if (!this.navId || !this.tabId || !state || is_was_active) {
6653             return;
6654         }
6655         
6656         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6657         if (!tg) {
6658             return;
6659         }
6660         var pan = tg.getPanelByName(this.tabId);
6661         if (!pan) {
6662             return;
6663         }
6664         // if we can not flip to new panel - go back to old nav highlight..
6665         if (false == tg.showPanel(pan)) {
6666             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6667             if (nv) {
6668                 var onav = nv.getWasActive();
6669                 if (onav) {
6670                     onav.setActive(true, false, true);
6671                 }
6672             }
6673             
6674         }
6675         
6676         
6677         
6678     },
6679      // this should not be here...
6680     setDisabled : function(state)
6681     {
6682         this.disabled = state;
6683         if (!state ) {
6684             this.el.removeClass('disabled');
6685         } else if (!this.el.hasClass('disabled')) {
6686             this.el.addClass('disabled');
6687         }
6688         
6689     },
6690     
6691     /**
6692      * Fetch the element to display the tooltip on.
6693      * @return {Roo.Element} defaults to this.el
6694      */
6695     tooltipEl : function()
6696     {
6697         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6698     },
6699     
6700     scrollToElement : function(e)
6701     {
6702         var c = document.body;
6703         
6704         /*
6705          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6706          */
6707         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6708             c = document.documentElement;
6709         }
6710         
6711         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6712         
6713         if(!target){
6714             return;
6715         }
6716
6717         var o = target.calcOffsetsTo(c);
6718         
6719         var options = {
6720             target : target,
6721             value : o[1]
6722         };
6723         
6724         this.fireEvent('scrollto', this, options, e);
6725         
6726         Roo.get(c).scrollTo('top', options.value, true);
6727         
6728         return;
6729     },
6730     /**
6731      * Set the HTML (text content) of the item
6732      * @param {string} html  content for the nav item
6733      */
6734     setHtml : function(html)
6735     {
6736         this.html = html;
6737         this.htmlEl.dom.innerHTML = html;
6738         
6739     } 
6740 });
6741  
6742
6743  /*
6744  * - LGPL
6745  *
6746  * sidebar item
6747  *
6748  *  li
6749  *    <span> icon </span>
6750  *    <span> text </span>
6751  *    <span>badge </span>
6752  */
6753
6754 /**
6755  * @class Roo.bootstrap.nav.SidebarItem
6756  * @extends Roo.bootstrap.nav.Item
6757  * Bootstrap Navbar.NavSidebarItem class
6758  * 
6759  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6760  * {Boolean} open is the menu open
6761  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6762  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6763  * {String} buttonSize (sm|md|lg)the extra classes for the button
6764  * {Boolean} showArrow show arrow next to the text (default true)
6765  * @constructor
6766  * Create a new Navbar Button
6767  * @param {Object} config The config object
6768  */
6769 Roo.bootstrap.nav.SidebarItem = function(config){
6770     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6771     this.addEvents({
6772         // raw events
6773         /**
6774          * @event click
6775          * The raw click event for the entire grid.
6776          * @param {Roo.EventObject} e
6777          */
6778         "click" : true,
6779          /**
6780             * @event changed
6781             * Fires when the active item active state changes
6782             * @param {Roo.bootstrap.nav.SidebarItem} this
6783             * @param {boolean} state the new state
6784              
6785          */
6786         'changed': true
6787     });
6788    
6789 };
6790
6791 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6792     
6793     badgeWeight : 'default',
6794     
6795     open: false,
6796     
6797     buttonView : false,
6798     
6799     buttonWeight : 'default',
6800     
6801     buttonSize : 'md',
6802     
6803     showArrow : true,
6804     
6805     getAutoCreate : function(){
6806         
6807         
6808         var a = {
6809                 tag: 'a',
6810                 href : this.href || '#',
6811                 cls: '',
6812                 html : '',
6813                 cn : []
6814         };
6815         
6816         if(this.buttonView){
6817             a = {
6818                 tag: 'button',
6819                 href : this.href || '#',
6820                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6821                 html : this.html,
6822                 cn : []
6823             };
6824         }
6825         
6826         var cfg = {
6827             tag: 'li',
6828             cls: '',
6829             cn: [ a ]
6830         };
6831         
6832         if (this.active) {
6833             cfg.cls += ' active';
6834         }
6835         
6836         if (this.disabled) {
6837             cfg.cls += ' disabled';
6838         }
6839         if (this.open) {
6840             cfg.cls += ' open x-open';
6841         }
6842         // left icon..
6843         if (this.glyphicon || this.icon) {
6844             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6845             a.cn.push({ tag : 'i', cls : c }) ;
6846         }
6847         
6848         if(!this.buttonView){
6849             var span = {
6850                 tag: 'span',
6851                 html : this.html || ''
6852             };
6853
6854             a.cn.push(span);
6855             
6856         }
6857         
6858         if (this.badge !== '') {
6859             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6860         }
6861         
6862         if (this.menu) {
6863             
6864             if(this.showArrow){
6865                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6866             }
6867             
6868             a.cls += ' dropdown-toggle treeview' ;
6869         }
6870         
6871         return cfg;
6872     },
6873     
6874     initEvents : function()
6875     { 
6876         if (typeof (this.menu) != 'undefined') {
6877             this.menu.parentType = this.xtype;
6878             this.menu.triggerEl = this.el;
6879             this.menu = this.addxtype(Roo.apply({}, this.menu));
6880         }
6881         
6882         this.el.on('click', this.onClick, this);
6883         
6884         if(this.badge !== ''){
6885             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6886         }
6887         
6888     },
6889     
6890     onClick : function(e)
6891     {
6892         if(this.disabled){
6893             e.preventDefault();
6894             return;
6895         }
6896         
6897         if(this.preventDefault){
6898             e.preventDefault();
6899         }
6900         
6901         this.fireEvent('click', this, e);
6902     },
6903     
6904     disable : function()
6905     {
6906         this.setDisabled(true);
6907     },
6908     
6909     enable : function()
6910     {
6911         this.setDisabled(false);
6912     },
6913     
6914     setDisabled : function(state)
6915     {
6916         if(this.disabled == state){
6917             return;
6918         }
6919         
6920         this.disabled = state;
6921         
6922         if (state) {
6923             this.el.addClass('disabled');
6924             return;
6925         }
6926         
6927         this.el.removeClass('disabled');
6928         
6929         return;
6930     },
6931     
6932     setActive : function(state)
6933     {
6934         if(this.active == state){
6935             return;
6936         }
6937         
6938         this.active = state;
6939         
6940         if (state) {
6941             this.el.addClass('active');
6942             return;
6943         }
6944         
6945         this.el.removeClass('active');
6946         
6947         return;
6948     },
6949     
6950     isActive: function () 
6951     {
6952         return this.active;
6953     },
6954     
6955     setBadge : function(str)
6956     {
6957         if(!this.badgeEl){
6958             return;
6959         }
6960         
6961         this.badgeEl.dom.innerHTML = str;
6962     }
6963     
6964    
6965      
6966  
6967 });
6968  
6969
6970  /*
6971  * - LGPL
6972  *
6973  * nav progress bar
6974  * 
6975  */
6976
6977 /**
6978  * @class Roo.bootstrap.nav.ProgressBar
6979  * @extends Roo.bootstrap.Component
6980  * @children Roo.bootstrap.nav.ProgressBarItem
6981  * Bootstrap NavProgressBar class
6982  * 
6983  * @constructor
6984  * Create a new nav progress bar - a bar indicating step along a process
6985  * @param {Object} config The config object
6986  */
6987
6988 Roo.bootstrap.nav.ProgressBar = function(config){
6989     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6990
6991     this.bullets = this.bullets || [];
6992    
6993 //    Roo.bootstrap.nav.ProgressBar.register(this);
6994      this.addEvents({
6995         /**
6996              * @event changed
6997              * Fires when the active item changes
6998              * @param {Roo.bootstrap.nav.ProgressBar} this
6999              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
7000              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
7001          */
7002         'changed': true
7003      });
7004     
7005 };
7006
7007 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7008     /**
7009      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7010      * Bullets for the Nav Progress bar for the toolbar
7011      */
7012     bullets : [],
7013     barItems : [],
7014     
7015     getAutoCreate : function()
7016     {
7017         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7018         
7019         cfg = {
7020             tag : 'div',
7021             cls : 'roo-navigation-bar-group',
7022             cn : [
7023                 {
7024                     tag : 'div',
7025                     cls : 'roo-navigation-top-bar'
7026                 },
7027                 {
7028                     tag : 'div',
7029                     cls : 'roo-navigation-bullets-bar',
7030                     cn : [
7031                         {
7032                             tag : 'ul',
7033                             cls : 'roo-navigation-bar'
7034                         }
7035                     ]
7036                 },
7037                 
7038                 {
7039                     tag : 'div',
7040                     cls : 'roo-navigation-bottom-bar'
7041                 }
7042             ]
7043             
7044         };
7045         
7046         return cfg;
7047         
7048     },
7049     
7050     initEvents: function() 
7051     {
7052         
7053     },
7054     
7055     onRender : function(ct, position) 
7056     {
7057         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7058         
7059         if(this.bullets.length){
7060             Roo.each(this.bullets, function(b){
7061                this.addItem(b);
7062             }, this);
7063         }
7064         
7065         this.format();
7066         
7067     },
7068     
7069     addItem : function(cfg)
7070     {
7071         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7072         
7073         item.parentId = this.id;
7074         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7075         
7076         if(cfg.html){
7077             var top = new Roo.bootstrap.Element({
7078                 tag : 'div',
7079                 cls : 'roo-navigation-bar-text'
7080             });
7081             
7082             var bottom = new Roo.bootstrap.Element({
7083                 tag : 'div',
7084                 cls : 'roo-navigation-bar-text'
7085             });
7086             
7087             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7088             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7089             
7090             var topText = new Roo.bootstrap.Element({
7091                 tag : 'span',
7092                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7093             });
7094             
7095             var bottomText = new Roo.bootstrap.Element({
7096                 tag : 'span',
7097                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7098             });
7099             
7100             topText.onRender(top.el, null);
7101             bottomText.onRender(bottom.el, null);
7102             
7103             item.topEl = top;
7104             item.bottomEl = bottom;
7105         }
7106         
7107         this.barItems.push(item);
7108         
7109         return item;
7110     },
7111     
7112     getActive : function()
7113     {
7114         var active = false;
7115         
7116         Roo.each(this.barItems, function(v){
7117             
7118             if (!v.isActive()) {
7119                 return;
7120             }
7121             
7122             active = v;
7123             return false;
7124             
7125         });
7126         
7127         return active;
7128     },
7129     
7130     setActiveItem : function(item)
7131     {
7132         var prev = false;
7133         
7134         Roo.each(this.barItems, function(v){
7135             if (v.rid == item.rid) {
7136                 return ;
7137             }
7138             
7139             if (v.isActive()) {
7140                 v.setActive(false);
7141                 prev = v;
7142             }
7143         });
7144
7145         item.setActive(true);
7146         
7147         this.fireEvent('changed', this, item, prev);
7148     },
7149     
7150     getBarItem: function(rid)
7151     {
7152         var ret = false;
7153         
7154         Roo.each(this.barItems, function(e) {
7155             if (e.rid != rid) {
7156                 return;
7157             }
7158             
7159             ret =  e;
7160             return false;
7161         });
7162         
7163         return ret;
7164     },
7165     
7166     indexOfItem : function(item)
7167     {
7168         var index = false;
7169         
7170         Roo.each(this.barItems, function(v, i){
7171             
7172             if (v.rid != item.rid) {
7173                 return;
7174             }
7175             
7176             index = i;
7177             return false
7178         });
7179         
7180         return index;
7181     },
7182     
7183     setActiveNext : function()
7184     {
7185         var i = this.indexOfItem(this.getActive());
7186         
7187         if (i > this.barItems.length) {
7188             return;
7189         }
7190         
7191         this.setActiveItem(this.barItems[i+1]);
7192     },
7193     
7194     setActivePrev : function()
7195     {
7196         var i = this.indexOfItem(this.getActive());
7197         
7198         if (i  < 1) {
7199             return;
7200         }
7201         
7202         this.setActiveItem(this.barItems[i-1]);
7203     },
7204     
7205     format : function()
7206     {
7207         if(!this.barItems.length){
7208             return;
7209         }
7210      
7211         var width = 100 / this.barItems.length;
7212         
7213         Roo.each(this.barItems, function(i){
7214             i.el.setStyle('width', width + '%');
7215             i.topEl.el.setStyle('width', width + '%');
7216             i.bottomEl.el.setStyle('width', width + '%');
7217         }, this);
7218         
7219     }
7220     
7221 });
7222 /*
7223  * - LGPL
7224  *
7225  * Nav Progress Item
7226  * 
7227  */
7228
7229 /**
7230  * @class Roo.bootstrap.nav.ProgressBarItem
7231  * @extends Roo.bootstrap.Component
7232  * Bootstrap NavProgressBarItem class
7233  * @cfg {String} rid the reference id
7234  * @cfg {Boolean} active (true|false) Is item active default false
7235  * @cfg {Boolean} disabled (true|false) Is item active default false
7236  * @cfg {String} html
7237  * @cfg {String} position (top|bottom) text position default bottom
7238  * @cfg {String} icon show icon instead of number
7239  * 
7240  * @constructor
7241  * Create a new NavProgressBarItem
7242  * @param {Object} config The config object
7243  */
7244 Roo.bootstrap.nav.ProgressBarItem = function(config){
7245     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7246     this.addEvents({
7247         // raw events
7248         /**
7249          * @event click
7250          * The raw click event for the entire grid.
7251          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7252          * @param {Roo.EventObject} e
7253          */
7254         "click" : true
7255     });
7256    
7257 };
7258
7259 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7260     
7261     rid : '',
7262     active : false,
7263     disabled : false,
7264     html : '',
7265     position : 'bottom',
7266     icon : false,
7267     
7268     getAutoCreate : function()
7269     {
7270         var iconCls = 'roo-navigation-bar-item-icon';
7271         
7272         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7273         
7274         var cfg = {
7275             tag: 'li',
7276             cls: 'roo-navigation-bar-item',
7277             cn : [
7278                 {
7279                     tag : 'i',
7280                     cls : iconCls
7281                 }
7282             ]
7283         };
7284         
7285         if(this.active){
7286             cfg.cls += ' active';
7287         }
7288         if(this.disabled){
7289             cfg.cls += ' disabled';
7290         }
7291         
7292         return cfg;
7293     },
7294     
7295     disable : function()
7296     {
7297         this.setDisabled(true);
7298     },
7299     
7300     enable : function()
7301     {
7302         this.setDisabled(false);
7303     },
7304     
7305     initEvents: function() 
7306     {
7307         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7308         
7309         this.iconEl.on('click', this.onClick, this);
7310     },
7311     
7312     onClick : function(e)
7313     {
7314         e.preventDefault();
7315         
7316         if(this.disabled){
7317             return;
7318         }
7319         
7320         if(this.fireEvent('click', this, e) === false){
7321             return;
7322         };
7323         
7324         this.parent().setActiveItem(this);
7325     },
7326     
7327     isActive: function () 
7328     {
7329         return this.active;
7330     },
7331     
7332     setActive : function(state)
7333     {
7334         if(this.active == state){
7335             return;
7336         }
7337         
7338         this.active = state;
7339         
7340         if (state) {
7341             this.el.addClass('active');
7342             return;
7343         }
7344         
7345         this.el.removeClass('active');
7346         
7347         return;
7348     },
7349     
7350     setDisabled : function(state)
7351     {
7352         if(this.disabled == state){
7353             return;
7354         }
7355         
7356         this.disabled = state;
7357         
7358         if (state) {
7359             this.el.addClass('disabled');
7360             return;
7361         }
7362         
7363         this.el.removeClass('disabled');
7364     },
7365     
7366     tooltipEl : function()
7367     {
7368         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7369     }
7370 });
7371  
7372
7373  /*
7374  * - LGPL
7375  *
7376  *  Breadcrumb Nav
7377  * 
7378  */
7379 Roo.namespace('Roo.bootstrap.breadcrumb');
7380
7381
7382 /**
7383  * @class Roo.bootstrap.breadcrumb.Nav
7384  * @extends Roo.bootstrap.Component
7385  * Bootstrap Breadcrumb Nav Class
7386  *  
7387  * @children Roo.bootstrap.breadcrumb.Item
7388  * 
7389  * @constructor
7390  * Create a new breadcrumb.Nav
7391  * @param {Object} config The config object
7392  */
7393
7394
7395 Roo.bootstrap.breadcrumb.Nav = function(config){
7396     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7397     
7398     
7399 };
7400
7401 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7402     
7403     getAutoCreate : function()
7404     {
7405
7406         var cfg = {
7407             tag: 'nav',
7408             cn : [
7409                 {
7410                     tag : 'ol',
7411                     cls : 'breadcrumb'
7412                 }
7413             ]
7414             
7415         };
7416           
7417         return cfg;
7418     },
7419     
7420     initEvents: function()
7421     {
7422         this.olEl = this.el.select('ol',true).first();    
7423     },
7424     getChildContainer : function()
7425     {
7426         return this.olEl;  
7427     }
7428     
7429 });
7430
7431  /*
7432  * - LGPL
7433  *
7434  *  Breadcrumb Item
7435  * 
7436  */
7437
7438
7439 /**
7440  * @class Roo.bootstrap.breadcrumb.Nav
7441  * @extends Roo.bootstrap.Component
7442  * @children Roo.bootstrap.Component
7443  * @parent Roo.bootstrap.breadcrumb.Nav
7444  * Bootstrap Breadcrumb Nav Class
7445  *  
7446  * 
7447  * @cfg {String} html the content of the link.
7448  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7449  * @cfg {Boolean} active is it active
7450
7451  * 
7452  * @constructor
7453  * Create a new breadcrumb.Nav
7454  * @param {Object} config The config object
7455  */
7456
7457 Roo.bootstrap.breadcrumb.Item = function(config){
7458     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7459     this.addEvents({
7460         // img events
7461         /**
7462          * @event click
7463          * The img click event for the img.
7464          * @param {Roo.EventObject} e
7465          */
7466         "click" : true
7467     });
7468     
7469 };
7470
7471 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7472     
7473     href: false,
7474     html : '',
7475     
7476     getAutoCreate : function()
7477     {
7478
7479         var cfg = {
7480             tag: 'li',
7481             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7482         };
7483         if (this.href !== false) {
7484             cfg.cn = [{
7485                 tag : 'a',
7486                 href : this.href,
7487                 html : this.html
7488             }];
7489         } else {
7490             cfg.html = this.html;
7491         }
7492         
7493         return cfg;
7494     },
7495     
7496     initEvents: function()
7497     {
7498         if (this.href) {
7499             this.el.select('a', true).first().on('click',this.onClick, this)
7500         }
7501         
7502     },
7503     onClick : function(e)
7504     {
7505         e.preventDefault();
7506         this.fireEvent('click',this,  e);
7507     }
7508     
7509 });
7510
7511  /*
7512  * - LGPL
7513  *
7514  * row
7515  * 
7516  */
7517
7518 /**
7519  * @class Roo.bootstrap.Row
7520  * @extends Roo.bootstrap.Component
7521  * @children Roo.bootstrap.Component
7522  * Bootstrap Row class (contains columns...)
7523  * 
7524  * @constructor
7525  * Create a new Row
7526  * @param {Object} config The config object
7527  */
7528
7529 Roo.bootstrap.Row = function(config){
7530     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7531 };
7532
7533 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7534     
7535     getAutoCreate : function(){
7536        return {
7537             cls: 'row clearfix'
7538        };
7539     }
7540     
7541     
7542 });
7543
7544  
7545
7546  /*
7547  * - LGPL
7548  *
7549  * pagination
7550  * 
7551  */
7552
7553 /**
7554  * @class Roo.bootstrap.Pagination
7555  * @extends Roo.bootstrap.Component
7556  * @children Roo.bootstrap.Pagination
7557  * Bootstrap Pagination class
7558  * 
7559  * @cfg {String} size (xs|sm|md|lg|xl)
7560  * @cfg {Boolean} inverse 
7561  * 
7562  * @constructor
7563  * Create a new Pagination
7564  * @param {Object} config The config object
7565  */
7566
7567 Roo.bootstrap.Pagination = function(config){
7568     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7569 };
7570
7571 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7572     
7573     cls: false,
7574     size: false,
7575     inverse: false,
7576     
7577     getAutoCreate : function(){
7578         var cfg = {
7579             tag: 'ul',
7580                 cls: 'pagination'
7581         };
7582         if (this.inverse) {
7583             cfg.cls += ' inverse';
7584         }
7585         if (this.html) {
7586             cfg.html=this.html;
7587         }
7588         if (this.cls) {
7589             cfg.cls += " " + this.cls;
7590         }
7591         return cfg;
7592     }
7593    
7594 });
7595
7596  
7597
7598  /*
7599  * - LGPL
7600  *
7601  * Pagination item
7602  * 
7603  */
7604
7605
7606 /**
7607  * @class Roo.bootstrap.PaginationItem
7608  * @extends Roo.bootstrap.Component
7609  * Bootstrap PaginationItem class
7610  * @cfg {String} html text
7611  * @cfg {String} href the link
7612  * @cfg {Boolean} preventDefault (true | false) default true
7613  * @cfg {Boolean} active (true | false) default false
7614  * @cfg {Boolean} disabled default false
7615  * 
7616  * 
7617  * @constructor
7618  * Create a new PaginationItem
7619  * @param {Object} config The config object
7620  */
7621
7622
7623 Roo.bootstrap.PaginationItem = function(config){
7624     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7625     this.addEvents({
7626         // raw events
7627         /**
7628          * @event click
7629          * The raw click event for the entire grid.
7630          * @param {Roo.EventObject} e
7631          */
7632         "click" : true
7633     });
7634 };
7635
7636 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7637     
7638     href : false,
7639     html : false,
7640     preventDefault: true,
7641     active : false,
7642     cls : false,
7643     disabled: false,
7644     
7645     getAutoCreate : function(){
7646         var cfg= {
7647             tag: 'li',
7648             cn: [
7649                 {
7650                     tag : 'a',
7651                     href : this.href ? this.href : '#',
7652                     html : this.html ? this.html : ''
7653                 }
7654             ]
7655         };
7656         
7657         if(this.cls){
7658             cfg.cls = this.cls;
7659         }
7660         
7661         if(this.disabled){
7662             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7663         }
7664         
7665         if(this.active){
7666             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7667         }
7668         
7669         return cfg;
7670     },
7671     
7672     initEvents: function() {
7673         
7674         this.el.on('click', this.onClick, this);
7675         
7676     },
7677     onClick : function(e)
7678     {
7679         Roo.log('PaginationItem on click ');
7680         if(this.preventDefault){
7681             e.preventDefault();
7682         }
7683         
7684         if(this.disabled){
7685             return;
7686         }
7687         
7688         this.fireEvent('click', this, e);
7689     }
7690    
7691 });
7692
7693  
7694
7695  /*
7696  * - LGPL
7697  *
7698  * slider
7699  * 
7700  */
7701
7702
7703 /**
7704  * @class Roo.bootstrap.Slider
7705  * @extends Roo.bootstrap.Component
7706  * Bootstrap Slider class
7707  *    
7708  * @constructor
7709  * Create a new Slider
7710  * @param {Object} config The config object
7711  */
7712
7713 Roo.bootstrap.Slider = function(config){
7714     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7715 };
7716
7717 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7718     
7719     getAutoCreate : function(){
7720         
7721         var cfg = {
7722             tag: 'div',
7723             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7724             cn: [
7725                 {
7726                     tag: 'a',
7727                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7728                 }
7729             ]
7730         };
7731         
7732         return cfg;
7733     }
7734    
7735 });
7736
7737  /*
7738  * Based on:
7739  * Ext JS Library 1.1.1
7740  * Copyright(c) 2006-2007, Ext JS, LLC.
7741  *
7742  * Originally Released Under LGPL - original licence link has changed is not relivant.
7743  *
7744  * Fork - LGPL
7745  * <script type="text/javascript">
7746  */
7747  /**
7748  * @extends Roo.dd.DDProxy
7749  * @class Roo.grid.SplitDragZone
7750  * Support for Column Header resizing
7751  * @constructor
7752  * @param {Object} config
7753  */
7754 // private
7755 // This is a support class used internally by the Grid components
7756 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7757     this.grid = grid;
7758     this.view = grid.getView();
7759     this.proxy = this.view.resizeProxy;
7760     Roo.grid.SplitDragZone.superclass.constructor.call(
7761         this,
7762         hd, // ID
7763         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7764         {  // CONFIG
7765             dragElId : Roo.id(this.proxy.dom),
7766             resizeFrame:false
7767         }
7768     );
7769     
7770     this.setHandleElId(Roo.id(hd));
7771     if (hd2 !== false) {
7772         this.setOuterHandleElId(Roo.id(hd2));
7773     }
7774     
7775     this.scroll = false;
7776 };
7777 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7778     fly: Roo.Element.fly,
7779
7780     b4StartDrag : function(x, y){
7781         this.view.headersDisabled = true;
7782         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7783                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7784         );
7785         this.proxy.setHeight(h);
7786         
7787         // for old system colWidth really stored the actual width?
7788         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7789         // which in reality did not work.. - it worked only for fixed sizes
7790         // for resizable we need to use actual sizes.
7791         var w = this.cm.getColumnWidth(this.cellIndex);
7792         if (!this.view.mainWrap) {
7793             // bootstrap.
7794             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7795         }
7796         
7797         
7798         
7799         // this was w-this.grid.minColumnWidth;
7800         // doesnt really make sense? - w = thie curren width or the rendered one?
7801         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7802         this.resetConstraints();
7803         this.setXConstraint(minw, 1000);
7804         this.setYConstraint(0, 0);
7805         this.minX = x - minw;
7806         this.maxX = x + 1000;
7807         this.startPos = x;
7808         if (!this.view.mainWrap) { // this is Bootstrap code..
7809             this.getDragEl().style.display='block';
7810         }
7811         
7812         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7813     },
7814
7815
7816     handleMouseDown : function(e){
7817         ev = Roo.EventObject.setEvent(e);
7818         var t = this.fly(ev.getTarget());
7819         if(t.hasClass("x-grid-split")){
7820             this.cellIndex = this.view.getCellIndex(t.dom);
7821             this.split = t.dom;
7822             this.cm = this.grid.colModel;
7823             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7824                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7825             }
7826         }
7827     },
7828
7829     endDrag : function(e){
7830         this.view.headersDisabled = false;
7831         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7832         var diff = endX - this.startPos;
7833         // 
7834         var w = this.cm.getColumnWidth(this.cellIndex);
7835         if (!this.view.mainWrap) {
7836             w = 0;
7837         }
7838         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7839     },
7840
7841     autoOffset : function(){
7842         this.setDelta(0,0);
7843     }
7844 });/*
7845  * Based on:
7846  * Ext JS Library 1.1.1
7847  * Copyright(c) 2006-2007, Ext JS, LLC.
7848  *
7849  * Originally Released Under LGPL - original licence link has changed is not relivant.
7850  *
7851  * Fork - LGPL
7852  * <script type="text/javascript">
7853  */
7854
7855 /**
7856  * @class Roo.grid.AbstractSelectionModel
7857  * @extends Roo.util.Observable
7858  * @abstract
7859  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7860  * implemented by descendant classes.  This class should not be directly instantiated.
7861  * @constructor
7862  */
7863 Roo.grid.AbstractSelectionModel = function(){
7864     this.locked = false;
7865     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7866 };
7867
7868 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7869     /** @ignore Called by the grid automatically. Do not call directly. */
7870     init : function(grid){
7871         this.grid = grid;
7872         this.initEvents();
7873     },
7874
7875     /**
7876      * Locks the selections.
7877      */
7878     lock : function(){
7879         this.locked = true;
7880     },
7881
7882     /**
7883      * Unlocks the selections.
7884      */
7885     unlock : function(){
7886         this.locked = false;
7887     },
7888
7889     /**
7890      * Returns true if the selections are locked.
7891      * @return {Boolean}
7892      */
7893     isLocked : function(){
7894         return this.locked;
7895     }
7896 });/*
7897  * Based on:
7898  * Ext JS Library 1.1.1
7899  * Copyright(c) 2006-2007, Ext JS, LLC.
7900  *
7901  * Originally Released Under LGPL - original licence link has changed is not relivant.
7902  *
7903  * Fork - LGPL
7904  * <script type="text/javascript">
7905  */
7906 /**
7907  * @extends Roo.grid.AbstractSelectionModel
7908  * @class Roo.grid.RowSelectionModel
7909  * The default SelectionModel used by {@link Roo.grid.Grid}.
7910  * It supports multiple selections and keyboard selection/navigation. 
7911  * @constructor
7912  * @param {Object} config
7913  */
7914 Roo.grid.RowSelectionModel = function(config){
7915     Roo.apply(this, config);
7916     this.selections = new Roo.util.MixedCollection(false, function(o){
7917         return o.id;
7918     });
7919
7920     this.last = false;
7921     this.lastActive = false;
7922
7923     this.addEvents({
7924         /**
7925         * @event selectionchange
7926         * Fires when the selection changes
7927         * @param {SelectionModel} this
7928         */
7929        "selectionchange" : true,
7930        /**
7931         * @event afterselectionchange
7932         * Fires after the selection changes (eg. by key press or clicking)
7933         * @param {SelectionModel} this
7934         */
7935        "afterselectionchange" : true,
7936        /**
7937         * @event beforerowselect
7938         * Fires when a row is selected being selected, return false to cancel.
7939         * @param {SelectionModel} this
7940         * @param {Number} rowIndex The selected index
7941         * @param {Boolean} keepExisting False if other selections will be cleared
7942         */
7943        "beforerowselect" : true,
7944        /**
7945         * @event rowselect
7946         * Fires when a row is selected.
7947         * @param {SelectionModel} this
7948         * @param {Number} rowIndex The selected index
7949         * @param {Roo.data.Record} r The record
7950         */
7951        "rowselect" : true,
7952        /**
7953         * @event rowdeselect
7954         * Fires when a row is deselected.
7955         * @param {SelectionModel} this
7956         * @param {Number} rowIndex The selected index
7957         */
7958         "rowdeselect" : true
7959     });
7960     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7961     this.locked = false;
7962 };
7963
7964 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7965     /**
7966      * @cfg {Boolean} singleSelect
7967      * True to allow selection of only one row at a time (defaults to false)
7968      */
7969     singleSelect : false,
7970
7971     // private
7972     initEvents : function(){
7973
7974         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7975             this.grid.on("mousedown", this.handleMouseDown, this);
7976         }else{ // allow click to work like normal
7977             this.grid.on("rowclick", this.handleDragableRowClick, this);
7978         }
7979         // bootstrap does not have a view..
7980         var view = this.grid.view ? this.grid.view : this.grid;
7981         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7982             "up" : function(e){
7983                 if(!e.shiftKey){
7984                     this.selectPrevious(e.shiftKey);
7985                 }else if(this.last !== false && this.lastActive !== false){
7986                     var last = this.last;
7987                     this.selectRange(this.last,  this.lastActive-1);
7988                     view.focusRow(this.lastActive);
7989                     if(last !== false){
7990                         this.last = last;
7991                     }
7992                 }else{
7993                     this.selectFirstRow();
7994                 }
7995                 this.fireEvent("afterselectionchange", this);
7996             },
7997             "down" : function(e){
7998                 if(!e.shiftKey){
7999                     this.selectNext(e.shiftKey);
8000                 }else if(this.last !== false && this.lastActive !== false){
8001                     var last = this.last;
8002                     this.selectRange(this.last,  this.lastActive+1);
8003                     view.focusRow(this.lastActive);
8004                     if(last !== false){
8005                         this.last = last;
8006                     }
8007                 }else{
8008                     this.selectFirstRow();
8009                 }
8010                 this.fireEvent("afterselectionchange", this);
8011             },
8012             scope: this
8013         });
8014
8015          
8016         view.on("refresh", this.onRefresh, this);
8017         view.on("rowupdated", this.onRowUpdated, this);
8018         view.on("rowremoved", this.onRemove, this);
8019     },
8020
8021     // private
8022     onRefresh : function(){
8023         var ds = this.grid.ds, i, v = this.grid.view;
8024         var s = this.selections;
8025         s.each(function(r){
8026             if((i = ds.indexOfId(r.id)) != -1){
8027                 v.onRowSelect(i);
8028                 s.add(ds.getAt(i)); // updating the selection relate data
8029             }else{
8030                 s.remove(r);
8031             }
8032         });
8033     },
8034
8035     // private
8036     onRemove : function(v, index, r){
8037         this.selections.remove(r);
8038     },
8039
8040     // private
8041     onRowUpdated : function(v, index, r){
8042         if(this.isSelected(r)){
8043             v.onRowSelect(index);
8044         }
8045     },
8046
8047     /**
8048      * Select records.
8049      * @param {Array} records The records to select
8050      * @param {Boolean} keepExisting (optional) True to keep existing selections
8051      */
8052     selectRecords : function(records, keepExisting){
8053         if(!keepExisting){
8054             this.clearSelections();
8055         }
8056         var ds = this.grid.ds;
8057         for(var i = 0, len = records.length; i < len; i++){
8058             this.selectRow(ds.indexOf(records[i]), true);
8059         }
8060     },
8061
8062     /**
8063      * Gets the number of selected rows.
8064      * @return {Number}
8065      */
8066     getCount : function(){
8067         return this.selections.length;
8068     },
8069
8070     /**
8071      * Selects the first row in the grid.
8072      */
8073     selectFirstRow : function(){
8074         this.selectRow(0);
8075     },
8076
8077     /**
8078      * Select the last row.
8079      * @param {Boolean} keepExisting (optional) True to keep existing selections
8080      */
8081     selectLastRow : function(keepExisting){
8082         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8083     },
8084
8085     /**
8086      * Selects the row immediately following the last selected row.
8087      * @param {Boolean} keepExisting (optional) True to keep existing selections
8088      */
8089     selectNext : function(keepExisting){
8090         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8091             this.selectRow(this.last+1, keepExisting);
8092             var view = this.grid.view ? this.grid.view : this.grid;
8093             view.focusRow(this.last);
8094         }
8095     },
8096
8097     /**
8098      * Selects the row that precedes the last selected row.
8099      * @param {Boolean} keepExisting (optional) True to keep existing selections
8100      */
8101     selectPrevious : function(keepExisting){
8102         if(this.last){
8103             this.selectRow(this.last-1, keepExisting);
8104             var view = this.grid.view ? this.grid.view : this.grid;
8105             view.focusRow(this.last);
8106         }
8107     },
8108
8109     /**
8110      * Returns the selected records
8111      * @return {Array} Array of selected records
8112      */
8113     getSelections : function(){
8114         return [].concat(this.selections.items);
8115     },
8116
8117     /**
8118      * Returns the first selected record.
8119      * @return {Record}
8120      */
8121     getSelected : function(){
8122         return this.selections.itemAt(0);
8123     },
8124
8125
8126     /**
8127      * Clears all selections.
8128      */
8129     clearSelections : function(fast){
8130         if(this.locked) {
8131             return;
8132         }
8133         if(fast !== true){
8134             var ds = this.grid.ds;
8135             var s = this.selections;
8136             s.each(function(r){
8137                 this.deselectRow(ds.indexOfId(r.id));
8138             }, this);
8139             s.clear();
8140         }else{
8141             this.selections.clear();
8142         }
8143         this.last = false;
8144     },
8145
8146
8147     /**
8148      * Selects all rows.
8149      */
8150     selectAll : function(){
8151         if(this.locked) {
8152             return;
8153         }
8154         this.selections.clear();
8155         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8156             this.selectRow(i, true);
8157         }
8158     },
8159
8160     /**
8161      * Returns True if there is a selection.
8162      * @return {Boolean}
8163      */
8164     hasSelection : function(){
8165         return this.selections.length > 0;
8166     },
8167
8168     /**
8169      * Returns True if the specified row is selected.
8170      * @param {Number/Record} record The record or index of the record to check
8171      * @return {Boolean}
8172      */
8173     isSelected : function(index){
8174         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8175         return (r && this.selections.key(r.id) ? true : false);
8176     },
8177
8178     /**
8179      * Returns True if the specified record id is selected.
8180      * @param {String} id The id of record to check
8181      * @return {Boolean}
8182      */
8183     isIdSelected : function(id){
8184         return (this.selections.key(id) ? true : false);
8185     },
8186
8187     // private
8188     handleMouseDown : function(e, t)
8189     {
8190         var view = this.grid.view ? this.grid.view : this.grid;
8191         var rowIndex;
8192         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8193             return;
8194         };
8195         if(e.shiftKey && this.last !== false){
8196             var last = this.last;
8197             this.selectRange(last, rowIndex, e.ctrlKey);
8198             this.last = last; // reset the last
8199             view.focusRow(rowIndex);
8200         }else{
8201             var isSelected = this.isSelected(rowIndex);
8202             if(e.button !== 0 && isSelected){
8203                 view.focusRow(rowIndex);
8204             }else if(e.ctrlKey && isSelected){
8205                 this.deselectRow(rowIndex);
8206             }else if(!isSelected){
8207                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8208                 view.focusRow(rowIndex);
8209             }
8210         }
8211         this.fireEvent("afterselectionchange", this);
8212     },
8213     // private
8214     handleDragableRowClick :  function(grid, rowIndex, e) 
8215     {
8216         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8217             this.selectRow(rowIndex, false);
8218             var view = this.grid.view ? this.grid.view : this.grid;
8219             view.focusRow(rowIndex);
8220              this.fireEvent("afterselectionchange", this);
8221         }
8222     },
8223     
8224     /**
8225      * Selects multiple rows.
8226      * @param {Array} rows Array of the indexes of the row to select
8227      * @param {Boolean} keepExisting (optional) True to keep existing selections
8228      */
8229     selectRows : function(rows, keepExisting){
8230         if(!keepExisting){
8231             this.clearSelections();
8232         }
8233         for(var i = 0, len = rows.length; i < len; i++){
8234             this.selectRow(rows[i], true);
8235         }
8236     },
8237
8238     /**
8239      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8240      * @param {Number} startRow The index of the first row in the range
8241      * @param {Number} endRow The index of the last row in the range
8242      * @param {Boolean} keepExisting (optional) True to retain existing selections
8243      */
8244     selectRange : function(startRow, endRow, keepExisting){
8245         if(this.locked) {
8246             return;
8247         }
8248         if(!keepExisting){
8249             this.clearSelections();
8250         }
8251         if(startRow <= endRow){
8252             for(var i = startRow; i <= endRow; i++){
8253                 this.selectRow(i, true);
8254             }
8255         }else{
8256             for(var i = startRow; i >= endRow; i--){
8257                 this.selectRow(i, true);
8258             }
8259         }
8260     },
8261
8262     /**
8263      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8264      * @param {Number} startRow The index of the first row in the range
8265      * @param {Number} endRow The index of the last row in the range
8266      */
8267     deselectRange : function(startRow, endRow, preventViewNotify){
8268         if(this.locked) {
8269             return;
8270         }
8271         for(var i = startRow; i <= endRow; i++){
8272             this.deselectRow(i, preventViewNotify);
8273         }
8274     },
8275
8276     /**
8277      * Selects a row.
8278      * @param {Number} row The index of the row to select
8279      * @param {Boolean} keepExisting (optional) True to keep existing selections
8280      */
8281     selectRow : function(index, keepExisting, preventViewNotify){
8282         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8283             return;
8284         }
8285         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8286             if(!keepExisting || this.singleSelect){
8287                 this.clearSelections();
8288             }
8289             var r = this.grid.ds.getAt(index);
8290             this.selections.add(r);
8291             this.last = this.lastActive = index;
8292             if(!preventViewNotify){
8293                 var view = this.grid.view ? this.grid.view : this.grid;
8294                 view.onRowSelect(index);
8295             }
8296             this.fireEvent("rowselect", this, index, r);
8297             this.fireEvent("selectionchange", this);
8298         }
8299     },
8300
8301     /**
8302      * Deselects a row.
8303      * @param {Number} row The index of the row to deselect
8304      */
8305     deselectRow : function(index, preventViewNotify){
8306         if(this.locked) {
8307             return;
8308         }
8309         if(this.last == index){
8310             this.last = false;
8311         }
8312         if(this.lastActive == index){
8313             this.lastActive = false;
8314         }
8315         var r = this.grid.ds.getAt(index);
8316         this.selections.remove(r);
8317         if(!preventViewNotify){
8318             var view = this.grid.view ? this.grid.view : this.grid;
8319             view.onRowDeselect(index);
8320         }
8321         this.fireEvent("rowdeselect", this, index);
8322         this.fireEvent("selectionchange", this);
8323     },
8324
8325     // private
8326     restoreLast : function(){
8327         if(this._last){
8328             this.last = this._last;
8329         }
8330     },
8331
8332     // private
8333     acceptsNav : function(row, col, cm){
8334         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8335     },
8336
8337     // private
8338     onEditorKey : function(field, e){
8339         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8340         if(k == e.TAB){
8341             e.stopEvent();
8342             ed.completeEdit();
8343             if(e.shiftKey){
8344                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8345             }else{
8346                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8347             }
8348         }else if(k == e.ENTER && !e.ctrlKey){
8349             e.stopEvent();
8350             ed.completeEdit();
8351             if(e.shiftKey){
8352                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8353             }else{
8354                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8355             }
8356         }else if(k == e.ESC){
8357             ed.cancelEdit();
8358         }
8359         if(newCell){
8360             g.startEditing(newCell[0], newCell[1]);
8361         }
8362     }
8363 });/*
8364  * Based on:
8365  * Ext JS Library 1.1.1
8366  * Copyright(c) 2006-2007, Ext JS, LLC.
8367  *
8368  * Originally Released Under LGPL - original licence link has changed is not relivant.
8369  *
8370  * Fork - LGPL
8371  * <script type="text/javascript">
8372  */
8373  
8374
8375 /**
8376  * @class Roo.grid.ColumnModel
8377  * @extends Roo.util.Observable
8378  * This is the default implementation of a ColumnModel used by the Grid. It defines
8379  * the columns in the grid.
8380  * <br>Usage:<br>
8381  <pre><code>
8382  var colModel = new Roo.grid.ColumnModel([
8383         {header: "Ticker", width: 60, sortable: true, locked: true},
8384         {header: "Company Name", width: 150, sortable: true},
8385         {header: "Market Cap.", width: 100, sortable: true},
8386         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8387         {header: "Employees", width: 100, sortable: true, resizable: false}
8388  ]);
8389  </code></pre>
8390  * <p>
8391  
8392  * The config options listed for this class are options which may appear in each
8393  * individual column definition.
8394  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8395  * @constructor
8396  * @param {Object} config An Array of column config objects. See this class's
8397  * config objects for details.
8398 */
8399 Roo.grid.ColumnModel = function(config){
8400         /**
8401      * The config passed into the constructor
8402      */
8403     this.config = []; //config;
8404     this.lookup = {};
8405
8406     // if no id, create one
8407     // if the column does not have a dataIndex mapping,
8408     // map it to the order it is in the config
8409     for(var i = 0, len = config.length; i < len; i++){
8410         this.addColumn(config[i]);
8411         
8412     }
8413
8414     /**
8415      * The width of columns which have no width specified (defaults to 100)
8416      * @type Number
8417      */
8418     this.defaultWidth = 100;
8419
8420     /**
8421      * Default sortable of columns which have no sortable specified (defaults to false)
8422      * @type Boolean
8423      */
8424     this.defaultSortable = false;
8425
8426     this.addEvents({
8427         /**
8428              * @event widthchange
8429              * Fires when the width of a column changes.
8430              * @param {ColumnModel} this
8431              * @param {Number} columnIndex The column index
8432              * @param {Number} newWidth The new width
8433              */
8434             "widthchange": true,
8435         /**
8436              * @event headerchange
8437              * Fires when the text of a header changes.
8438              * @param {ColumnModel} this
8439              * @param {Number} columnIndex The column index
8440              * @param {Number} newText The new header text
8441              */
8442             "headerchange": true,
8443         /**
8444              * @event hiddenchange
8445              * Fires when a column is hidden or "unhidden".
8446              * @param {ColumnModel} this
8447              * @param {Number} columnIndex The column index
8448              * @param {Boolean} hidden true if hidden, false otherwise
8449              */
8450             "hiddenchange": true,
8451             /**
8452          * @event columnmoved
8453          * Fires when a column is moved.
8454          * @param {ColumnModel} this
8455          * @param {Number} oldIndex
8456          * @param {Number} newIndex
8457          */
8458         "columnmoved" : true,
8459         /**
8460          * @event columlockchange
8461          * Fires when a column's locked state is changed
8462          * @param {ColumnModel} this
8463          * @param {Number} colIndex
8464          * @param {Boolean} locked true if locked
8465          */
8466         "columnlockchange" : true
8467     });
8468     Roo.grid.ColumnModel.superclass.constructor.call(this);
8469 };
8470 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8471     /**
8472      * @cfg {String} header [required] The header text to display in the Grid view.
8473      */
8474         /**
8475      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8476      */
8477         /**
8478      * @cfg {String} smHeader Header at Bootsrap Small width
8479      */
8480         /**
8481      * @cfg {String} mdHeader Header at Bootsrap Medium width
8482      */
8483         /**
8484      * @cfg {String} lgHeader Header at Bootsrap Large width
8485      */
8486         /**
8487      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8488      */
8489     /**
8490      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8491      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8492      * specified, the column's index is used as an index into the Record's data Array.
8493      */
8494     /**
8495      * @cfg {Number} width  The initial width in pixels of the column. Using this
8496      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8497      */
8498     /**
8499      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8500      * Defaults to the value of the {@link #defaultSortable} property.
8501      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8502      */
8503     /**
8504      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8505      */
8506     /**
8507      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8508      */
8509     /**
8510      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8511      */
8512     /**
8513      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8514      */
8515     /**
8516      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8517      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8518      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8519      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8520      */
8521        /**
8522      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8523      */
8524     /**
8525      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8526      */
8527     /**
8528      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8529      */
8530     /**
8531      * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
8532      */
8533     /**
8534      * @cfg {String} tooltip mouse over tooltip text
8535      */
8536     /**
8537      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8538      */
8539     /**
8540      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8541      */
8542     /**
8543      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8544      */
8545     /**
8546      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8547      */
8548         /**
8549      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8550      */
8551     /**
8552      * Returns the id of the column at the specified index.
8553      * @param {Number} index The column index
8554      * @return {String} the id
8555      */
8556     getColumnId : function(index){
8557         return this.config[index].id;
8558     },
8559
8560     /**
8561      * Returns the column for a specified id.
8562      * @param {String} id The column id
8563      * @return {Object} the column
8564      */
8565     getColumnById : function(id){
8566         return this.lookup[id];
8567     },
8568
8569     
8570     /**
8571      * Returns the column Object for a specified dataIndex.
8572      * @param {String} dataIndex The column dataIndex
8573      * @return {Object|Boolean} the column or false if not found
8574      */
8575     getColumnByDataIndex: function(dataIndex){
8576         var index = this.findColumnIndex(dataIndex);
8577         return index > -1 ? this.config[index] : false;
8578     },
8579     
8580     /**
8581      * Returns the index for a specified column id.
8582      * @param {String} id The column id
8583      * @return {Number} the index, or -1 if not found
8584      */
8585     getIndexById : function(id){
8586         for(var i = 0, len = this.config.length; i < len; i++){
8587             if(this.config[i].id == id){
8588                 return i;
8589             }
8590         }
8591         return -1;
8592     },
8593     
8594     /**
8595      * Returns the index for a specified column dataIndex.
8596      * @param {String} dataIndex The column dataIndex
8597      * @return {Number} the index, or -1 if not found
8598      */
8599     
8600     findColumnIndex : function(dataIndex){
8601         for(var i = 0, len = this.config.length; i < len; i++){
8602             if(this.config[i].dataIndex == dataIndex){
8603                 return i;
8604             }
8605         }
8606         return -1;
8607     },
8608     
8609     
8610     moveColumn : function(oldIndex, newIndex){
8611         var c = this.config[oldIndex];
8612         this.config.splice(oldIndex, 1);
8613         this.config.splice(newIndex, 0, c);
8614         this.dataMap = null;
8615         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8616     },
8617
8618     isLocked : function(colIndex){
8619         return this.config[colIndex].locked === true;
8620     },
8621
8622     setLocked : function(colIndex, value, suppressEvent){
8623         if(this.isLocked(colIndex) == value){
8624             return;
8625         }
8626         this.config[colIndex].locked = value;
8627         if(!suppressEvent){
8628             this.fireEvent("columnlockchange", this, colIndex, value);
8629         }
8630     },
8631
8632     getTotalLockedWidth : function(){
8633         var totalWidth = 0;
8634         for(var i = 0; i < this.config.length; i++){
8635             if(this.isLocked(i) && !this.isHidden(i)){
8636                 this.totalWidth += this.getColumnWidth(i);
8637             }
8638         }
8639         return totalWidth;
8640     },
8641
8642     getLockedCount : function(){
8643         for(var i = 0, len = this.config.length; i < len; i++){
8644             if(!this.isLocked(i)){
8645                 return i;
8646             }
8647         }
8648         
8649         return this.config.length;
8650     },
8651
8652     /**
8653      * Returns the number of columns.
8654      * @return {Number}
8655      */
8656     getColumnCount : function(visibleOnly){
8657         if(visibleOnly === true){
8658             var c = 0;
8659             for(var i = 0, len = this.config.length; i < len; i++){
8660                 if(!this.isHidden(i)){
8661                     c++;
8662                 }
8663             }
8664             return c;
8665         }
8666         return this.config.length;
8667     },
8668
8669     /**
8670      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8671      * @param {Function} fn
8672      * @param {Object} scope (optional)
8673      * @return {Array} result
8674      */
8675     getColumnsBy : function(fn, scope){
8676         var r = [];
8677         for(var i = 0, len = this.config.length; i < len; i++){
8678             var c = this.config[i];
8679             if(fn.call(scope||this, c, i) === true){
8680                 r[r.length] = c;
8681             }
8682         }
8683         return r;
8684     },
8685
8686     /**
8687      * Returns true if the specified column is sortable.
8688      * @param {Number} col The column index
8689      * @return {Boolean}
8690      */
8691     isSortable : function(col){
8692         if(typeof this.config[col].sortable == "undefined"){
8693             return this.defaultSortable;
8694         }
8695         return this.config[col].sortable;
8696     },
8697
8698     /**
8699      * Returns the rendering (formatting) function defined for the column.
8700      * @param {Number} col The column index.
8701      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8702      */
8703     getRenderer : function(col){
8704         if(!this.config[col].renderer){
8705             return Roo.grid.ColumnModel.defaultRenderer;
8706         }
8707         return this.config[col].renderer;
8708     },
8709
8710     /**
8711      * Sets the rendering (formatting) function for a column.
8712      * @param {Number} col The column index
8713      * @param {Function} fn The function to use to process the cell's raw data
8714      * to return HTML markup for the grid view. The render function is called with
8715      * the following parameters:<ul>
8716      * <li>Data value.</li>
8717      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8718      * <li>css A CSS style string to apply to the table cell.</li>
8719      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8720      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8721      * <li>Row index</li>
8722      * <li>Column index</li>
8723      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8724      */
8725     setRenderer : function(col, fn){
8726         this.config[col].renderer = fn;
8727     },
8728
8729     /**
8730      * Returns the width for the specified column.
8731      * @param {Number} col The column index
8732      * @param (optional) {String} gridSize bootstrap width size.
8733      * @return {Number}
8734      */
8735     getColumnWidth : function(col, gridSize)
8736         {
8737                 var cfg = this.config[col];
8738                 
8739                 if (typeof(gridSize) == 'undefined') {
8740                         return cfg.width * 1 || this.defaultWidth;
8741                 }
8742                 if (gridSize === false) { // if we set it..
8743                         return cfg.width || false;
8744                 }
8745                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8746                 
8747                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8748                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8749                                 continue;
8750                         }
8751                         return cfg[ sizes[i] ];
8752                 }
8753                 return 1;
8754                 
8755     },
8756
8757     /**
8758      * Sets the width for a column.
8759      * @param {Number} col The column index
8760      * @param {Number} width The new width
8761      */
8762     setColumnWidth : function(col, width, suppressEvent){
8763         this.config[col].width = width;
8764         this.totalWidth = null;
8765         if(!suppressEvent){
8766              this.fireEvent("widthchange", this, col, width);
8767         }
8768     },
8769
8770     /**
8771      * Returns the total width of all columns.
8772      * @param {Boolean} includeHidden True to include hidden column widths
8773      * @return {Number}
8774      */
8775     getTotalWidth : function(includeHidden){
8776         if(!this.totalWidth){
8777             this.totalWidth = 0;
8778             for(var i = 0, len = this.config.length; i < len; i++){
8779                 if(includeHidden || !this.isHidden(i)){
8780                     this.totalWidth += this.getColumnWidth(i);
8781                 }
8782             }
8783         }
8784         return this.totalWidth;
8785     },
8786
8787     /**
8788      * Returns the header for the specified column.
8789      * @param {Number} col The column index
8790      * @return {String}
8791      */
8792     getColumnHeader : function(col){
8793         return this.config[col].header;
8794     },
8795
8796     /**
8797      * Sets the header for a column.
8798      * @param {Number} col The column index
8799      * @param {String} header The new header
8800      */
8801     setColumnHeader : function(col, header){
8802         this.config[col].header = header;
8803         this.fireEvent("headerchange", this, col, header);
8804     },
8805
8806     /**
8807      * Returns the tooltip for the specified column.
8808      * @param {Number} col The column index
8809      * @return {String}
8810      */
8811     getColumnTooltip : function(col){
8812             return this.config[col].tooltip;
8813     },
8814     /**
8815      * Sets the tooltip for a column.
8816      * @param {Number} col The column index
8817      * @param {String} tooltip The new tooltip
8818      */
8819     setColumnTooltip : function(col, tooltip){
8820             this.config[col].tooltip = tooltip;
8821     },
8822
8823     /**
8824      * Returns the dataIndex for the specified column.
8825      * @param {Number} col The column index
8826      * @return {Number}
8827      */
8828     getDataIndex : function(col){
8829         return this.config[col].dataIndex;
8830     },
8831
8832     /**
8833      * Sets the dataIndex for a column.
8834      * @param {Number} col The column index
8835      * @param {Number} dataIndex The new dataIndex
8836      */
8837     setDataIndex : function(col, dataIndex){
8838         this.config[col].dataIndex = dataIndex;
8839     },
8840
8841     
8842     
8843     /**
8844      * Returns true if the cell is editable.
8845      * @param {Number} colIndex The column index
8846      * @param {Number} rowIndex The row index - this is nto actually used..?
8847      * @return {Boolean}
8848      */
8849     isCellEditable : function(colIndex, rowIndex){
8850         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8851     },
8852
8853     /**
8854      * Returns the editor defined for the cell/column.
8855      * return false or null to disable editing.
8856      * @param {Number} colIndex The column index
8857      * @param {Number} rowIndex The row index
8858      * @return {Object}
8859      */
8860     getCellEditor : function(colIndex, rowIndex){
8861         return this.config[colIndex].editor;
8862     },
8863
8864     /**
8865      * Sets if a column is editable.
8866      * @param {Number} col The column index
8867      * @param {Boolean} editable True if the column is editable
8868      */
8869     setEditable : function(col, editable){
8870         this.config[col].editable = editable;
8871     },
8872
8873
8874     /**
8875      * Returns true if the column is hidden.
8876      * @param {Number} colIndex The column index
8877      * @return {Boolean}
8878      */
8879     isHidden : function(colIndex){
8880         return this.config[colIndex].hidden;
8881     },
8882
8883
8884     /**
8885      * Returns true if the column width cannot be changed
8886      */
8887     isFixed : function(colIndex){
8888         return this.config[colIndex].fixed;
8889     },
8890
8891     /**
8892      * Returns true if the column can be resized
8893      * @return {Boolean}
8894      */
8895     isResizable : function(colIndex){
8896         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8897     },
8898     /**
8899      * Sets if a column is hidden.
8900      * @param {Number} colIndex The column index
8901      * @param {Boolean} hidden True if the column is hidden
8902      */
8903     setHidden : function(colIndex, hidden){
8904         this.config[colIndex].hidden = hidden;
8905         this.totalWidth = null;
8906         this.fireEvent("hiddenchange", this, colIndex, hidden);
8907     },
8908
8909     /**
8910      * Sets the editor for a column.
8911      * @param {Number} col The column index
8912      * @param {Object} editor The editor object
8913      */
8914     setEditor : function(col, editor){
8915         this.config[col].editor = editor;
8916     },
8917     /**
8918      * Add a column (experimental...) - defaults to adding to the end..
8919      * @param {Object} config 
8920     */
8921     addColumn : function(c)
8922     {
8923     
8924         var i = this.config.length;
8925         this.config[i] = c;
8926         
8927         if(typeof c.dataIndex == "undefined"){
8928             c.dataIndex = i;
8929         }
8930         if(typeof c.renderer == "string"){
8931             c.renderer = Roo.util.Format[c.renderer];
8932         }
8933         if(typeof c.id == "undefined"){
8934             c.id = Roo.id();
8935         }
8936         if(c.editor && c.editor.xtype){
8937             c.editor  = Roo.factory(c.editor, Roo.grid);
8938         }
8939         if(c.editor && c.editor.isFormField){
8940             c.editor = new Roo.grid.GridEditor(c.editor);
8941         }
8942         this.lookup[c.id] = c;
8943     }
8944     
8945 });
8946
8947 Roo.grid.ColumnModel.defaultRenderer = function(value)
8948 {
8949     if(typeof value == "object") {
8950         return value;
8951     }
8952         if(typeof value == "string" && value.length < 1){
8953             return "&#160;";
8954         }
8955     
8956         return String.format("{0}", value);
8957 };
8958
8959 // Alias for backwards compatibility
8960 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8961 /*
8962  * Based on:
8963  * Ext JS Library 1.1.1
8964  * Copyright(c) 2006-2007, Ext JS, LLC.
8965  *
8966  * Originally Released Under LGPL - original licence link has changed is not relivant.
8967  *
8968  * Fork - LGPL
8969  * <script type="text/javascript">
8970  */
8971  
8972 /**
8973  * @class Roo.LoadMask
8974  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8975  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8976  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8977  * element's UpdateManager load indicator and will be destroyed after the initial load.
8978  * @constructor
8979  * Create a new LoadMask
8980  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8981  * @param {Object} config The config object
8982  */
8983 Roo.LoadMask = function(el, config){
8984     this.el = Roo.get(el);
8985     Roo.apply(this, config);
8986     if(this.store){
8987         this.store.on('beforeload', this.onBeforeLoad, this);
8988         this.store.on('load', this.onLoad, this);
8989         this.store.on('loadexception', this.onLoadException, this);
8990         this.removeMask = false;
8991     }else{
8992         var um = this.el.getUpdateManager();
8993         um.showLoadIndicator = false; // disable the default indicator
8994         um.on('beforeupdate', this.onBeforeLoad, this);
8995         um.on('update', this.onLoad, this);
8996         um.on('failure', this.onLoad, this);
8997         this.removeMask = true;
8998     }
8999 };
9000
9001 Roo.LoadMask.prototype = {
9002     /**
9003      * @cfg {Boolean} removeMask
9004      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9005      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9006      */
9007     removeMask : false,
9008     /**
9009      * @cfg {String} msg
9010      * The text to display in a centered loading message box (defaults to 'Loading...')
9011      */
9012     msg : 'Loading...',
9013     /**
9014      * @cfg {String} msgCls
9015      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9016      */
9017     msgCls : 'x-mask-loading',
9018
9019     /**
9020      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9021      * @type Boolean
9022      */
9023     disabled: false,
9024
9025     /**
9026      * Disables the mask to prevent it from being displayed
9027      */
9028     disable : function(){
9029        this.disabled = true;
9030     },
9031
9032     /**
9033      * Enables the mask so that it can be displayed
9034      */
9035     enable : function(){
9036         this.disabled = false;
9037     },
9038     
9039     onLoadException : function()
9040     {
9041         Roo.log(arguments);
9042         
9043         if (typeof(arguments[3]) != 'undefined') {
9044             Roo.MessageBox.alert("Error loading",arguments[3]);
9045         } 
9046         /*
9047         try {
9048             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9049                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9050             }   
9051         } catch(e) {
9052             
9053         }
9054         */
9055     
9056         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9057     },
9058     // private
9059     onLoad : function()
9060     {
9061         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9062     },
9063
9064     // private
9065     onBeforeLoad : function(){
9066         if(!this.disabled){
9067             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9068         }
9069     },
9070
9071     // private
9072     destroy : function(){
9073         if(this.store){
9074             this.store.un('beforeload', this.onBeforeLoad, this);
9075             this.store.un('load', this.onLoad, this);
9076             this.store.un('loadexception', this.onLoadException, this);
9077         }else{
9078             var um = this.el.getUpdateManager();
9079             um.un('beforeupdate', this.onBeforeLoad, this);
9080             um.un('update', this.onLoad, this);
9081             um.un('failure', this.onLoad, this);
9082         }
9083     }
9084 };/**
9085  * @class Roo.bootstrap.Table
9086  * @licence LGBL
9087  * @extends Roo.bootstrap.Component
9088  * @children Roo.bootstrap.TableBody
9089  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9090  * Similar to Roo.grid.Grid
9091  * <pre><code>
9092  var table = Roo.factory({
9093     xtype : 'Table',
9094     xns : Roo.bootstrap,
9095     autoSizeColumns: true,
9096     
9097     
9098     store : {
9099         xtype : 'Store',
9100         xns : Roo.data,
9101         remoteSort : true,
9102         sortInfo : { direction : 'ASC', field: 'name' },
9103         proxy : {
9104            xtype : 'HttpProxy',
9105            xns : Roo.data,
9106            method : 'GET',
9107            url : 'https://example.com/some.data.url.json'
9108         },
9109         reader : {
9110            xtype : 'JsonReader',
9111            xns : Roo.data,
9112            fields : [ 'id', 'name', whatever' ],
9113            id : 'id',
9114            root : 'data'
9115         }
9116     },
9117     cm : [
9118         {
9119             xtype : 'ColumnModel',
9120             xns : Roo.grid,
9121             align : 'center',
9122             cursor : 'pointer',
9123             dataIndex : 'is_in_group',
9124             header : "Name",
9125             sortable : true,
9126             renderer : function(v, x , r) {  
9127             
9128                 return String.format("{0}", v)
9129             }
9130             width : 3
9131         } // more columns..
9132     ],
9133     selModel : {
9134         xtype : 'RowSelectionModel',
9135         xns : Roo.bootstrap.Table
9136         // you can add listeners to catch selection change here....
9137     }
9138      
9139
9140  });
9141  // set any options
9142  grid.render(Roo.get("some-div"));
9143 </code></pre>
9144
9145 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9146
9147
9148
9149  *
9150  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9151  * @cfg {Roo.data.Store} store The data store to use
9152  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9153  * 
9154  * @cfg {String} cls table class
9155  *
9156  *
9157  * @cfg {string} empty_results  Text to display for no results 
9158  * @cfg {boolean} striped Should the rows be alternative striped
9159  * @cfg {boolean} bordered Add borders to the table
9160  * @cfg {boolean} hover Add hover highlighting
9161  * @cfg {boolean} condensed Format condensed
9162  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9163  *                also adds table-responsive (see bootstrap docs for details)
9164  * @cfg {Boolean} loadMask (true|false) default false
9165  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9166  * @cfg {Boolean} footerRow (true|false) generate tfoot with columns of values, default false
9167  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9168  * @cfg {Boolean} rowSelection (true|false) default false
9169  * @cfg {Boolean} cellSelection (true|false) default false
9170  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9171  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9172  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9173  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9174  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9175  * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9176  *
9177  * 
9178  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9179  * 
9180  * @constructor
9181  * Create a new Table
9182  * @param {Object} config The config object
9183  */
9184
9185 Roo.bootstrap.Table = function(config)
9186 {
9187     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9188      
9189     // BC...
9190     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9191     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9192     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9193     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9194     
9195     this.view = this; // compat with grid.
9196     
9197     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9198     if (this.sm) {
9199         this.sm.grid = this;
9200         this.selModel = Roo.factory(this.sm, Roo.grid);
9201         this.sm = this.selModel;
9202         this.sm.xmodule = this.xmodule || false;
9203     }
9204     
9205     if (this.cm && typeof(this.cm.config) == 'undefined') {
9206         this.colModel = new Roo.grid.ColumnModel(this.cm);
9207         this.cm = this.colModel;
9208         this.cm.xmodule = this.xmodule || false;
9209     }
9210     if (this.store) {
9211         this.store= Roo.factory(this.store, Roo.data);
9212         this.ds = this.store;
9213         this.ds.xmodule = this.xmodule || false;
9214          
9215     }
9216     if (this.footer && this.store) {
9217         this.footer.dataSource = this.ds;
9218         this.footer = Roo.factory(this.footer);
9219     }
9220     
9221     /** @private */
9222     this.addEvents({
9223         /**
9224          * @event cellclick
9225          * Fires when a cell is clicked
9226          * @param {Roo.bootstrap.Table} this
9227          * @param {Roo.Element} el
9228          * @param {Number} rowIndex
9229          * @param {Number} columnIndex
9230          * @param {Roo.EventObject} e
9231          */
9232         "cellclick" : true,
9233         /**
9234          * @event celldblclick
9235          * Fires when a cell is double clicked
9236          * @param {Roo.bootstrap.Table} this
9237          * @param {Roo.Element} el
9238          * @param {Number} rowIndex
9239          * @param {Number} columnIndex
9240          * @param {Roo.EventObject} e
9241          */
9242         "celldblclick" : true,
9243         /**
9244          * @event rowclick
9245          * Fires when a row is clicked
9246          * @param {Roo.bootstrap.Table} this
9247          * @param {Roo.Element} el
9248          * @param {Number} rowIndex
9249          * @param {Roo.EventObject} e
9250          */
9251         "rowclick" : true,
9252         /**
9253          * @event rowdblclick
9254          * Fires when a row is double clicked
9255          * @param {Roo.bootstrap.Table} this
9256          * @param {Roo.Element} el
9257          * @param {Number} rowIndex
9258          * @param {Roo.EventObject} e
9259          */
9260         "rowdblclick" : true,
9261         /**
9262          * @event mouseover
9263          * Fires when a mouseover occur
9264          * @param {Roo.bootstrap.Table} this
9265          * @param {Roo.Element} el
9266          * @param {Number} rowIndex
9267          * @param {Number} columnIndex
9268          * @param {Roo.EventObject} e
9269          */
9270         "mouseover" : true,
9271         /**
9272          * @event mouseout
9273          * Fires when a mouseout occur
9274          * @param {Roo.bootstrap.Table} this
9275          * @param {Roo.Element} el
9276          * @param {Number} rowIndex
9277          * @param {Number} columnIndex
9278          * @param {Roo.EventObject} e
9279          */
9280         "mouseout" : true,
9281         /**
9282          * @event rowclass
9283          * Fires when a row is rendered, so you can change add a style to it.
9284          * @param {Roo.bootstrap.Table} this
9285          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9286          */
9287         'rowclass' : true,
9288           /**
9289          * @event rowsrendered
9290          * Fires when all the  rows have been rendered
9291          * @param {Roo.bootstrap.Table} this
9292          */
9293         'rowsrendered' : true,
9294         /**
9295          * @event contextmenu
9296          * The raw contextmenu event for the entire grid.
9297          * @param {Roo.EventObject} e
9298          */
9299         "contextmenu" : true,
9300         /**
9301          * @event rowcontextmenu
9302          * Fires when a row is right clicked
9303          * @param {Roo.bootstrap.Table} this
9304          * @param {Number} rowIndex
9305          * @param {Roo.EventObject} e
9306          */
9307         "rowcontextmenu" : true,
9308         /**
9309          * @event cellcontextmenu
9310          * Fires when a cell is right clicked
9311          * @param {Roo.bootstrap.Table} this
9312          * @param {Number} rowIndex
9313          * @param {Number} cellIndex
9314          * @param {Roo.EventObject} e
9315          */
9316          "cellcontextmenu" : true,
9317          /**
9318          * @event headercontextmenu
9319          * Fires when a header is right clicked
9320          * @param {Roo.bootstrap.Table} this
9321          * @param {Number} columnIndex
9322          * @param {Roo.EventObject} e
9323          */
9324         "headercontextmenu" : true,
9325         /**
9326          * @event mousedown
9327          * The raw mousedown event for the entire grid.
9328          * @param {Roo.EventObject} e
9329          */
9330         "mousedown" : true
9331         
9332     });
9333 };
9334
9335 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9336     
9337     cls: false,
9338     
9339     empty_results : '',
9340     striped : false,
9341     scrollBody : false,
9342     bordered: false,
9343     hover:  false,
9344     condensed : false,
9345     responsive : false,
9346     sm : false,
9347     cm : false,
9348     store : false,
9349     loadMask : false,
9350     footerShow : true,
9351     footerRow : false,
9352     headerShow : true,
9353     enableColumnResize: true,
9354     disableAutoSize: false,
9355   
9356     rowSelection : false,
9357     cellSelection : false,
9358     layout : false,
9359
9360     minColumnWidth : 50,
9361     
9362     // Roo.Element - the tbody
9363     bodyEl: false,  // <tbody> Roo.Element - thead element    
9364     headEl: false,  // <thead> Roo.Element - thead element
9365     resizeProxy : false, // proxy element for dragging?
9366
9367
9368     
9369     container: false, // used by gridpanel...
9370     
9371     lazyLoad : false,
9372     
9373     CSS : Roo.util.CSS,
9374     
9375     auto_hide_footer : false,
9376     
9377     view: false, // actually points to this..
9378     
9379     getAutoCreate : function()
9380     {
9381         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9382         
9383         cfg = {
9384             tag: 'table',
9385             cls : 'table', 
9386             cn : []
9387         };
9388         // this get's auto added by panel.Grid
9389         if (this.scrollBody) {
9390             cfg.cls += ' table-body-fixed';
9391         }    
9392         if (this.striped) {
9393             cfg.cls += ' table-striped';
9394         }
9395         
9396         if (this.hover) {
9397             cfg.cls += ' table-hover';
9398         }
9399         if (this.bordered) {
9400             cfg.cls += ' table-bordered';
9401         }
9402         if (this.condensed) {
9403             cfg.cls += ' table-condensed';
9404         }
9405         
9406         if (this.responsive) {
9407             cfg.cls += ' table-responsive';
9408         }
9409         
9410         if (this.cls) {
9411             cfg.cls+=  ' ' +this.cls;
9412         }
9413         
9414         
9415         
9416         if (this.layout) {
9417             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9418         }
9419         
9420         if(this.store || this.cm){
9421             if(this.headerShow){
9422                 cfg.cn.push(this.renderHeader());
9423             }
9424             
9425             cfg.cn.push(this.renderBody());
9426             
9427             if(this.footerShow || this.footerRow){
9428                 cfg.cn.push(this.renderFooter());
9429             }
9430
9431             // where does this come from?
9432             //cfg.cls+=  ' TableGrid';
9433         }
9434         
9435         return { cn : [ cfg ] };
9436     },
9437     
9438     initEvents : function()
9439     {   
9440         if(!this.store || !this.cm){
9441             return;
9442         }
9443         if (this.selModel) {
9444             this.selModel.initEvents();
9445         }
9446         
9447         
9448         //Roo.log('initEvents with ds!!!!');
9449         
9450         this.bodyEl = this.el.select('tbody', true).first();
9451         this.headEl = this.el.select('thead', true).first();
9452         this.mainFoot = this.el.select('tfoot', true).first();
9453         
9454         
9455         
9456         
9457         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9458             e.on('click', this.sort, this);
9459         }, this);
9460         
9461         
9462         // why is this done????? = it breaks dialogs??
9463         //this.parent().el.setStyle('position', 'relative');
9464         
9465         
9466         if (this.footer) {
9467             this.footer.parentId = this.id;
9468             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9469             
9470             if(this.lazyLoad){
9471                 this.el.select('tfoot tr td').first().addClass('hide');
9472             }
9473         } 
9474         
9475         if(this.loadMask) {
9476             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9477         }
9478         
9479         this.store.on('load', this.onLoad, this);
9480         this.store.on('beforeload', this.onBeforeLoad, this);
9481         this.store.on('update', this.onUpdate, this);
9482         this.store.on('add', this.onAdd, this);
9483         this.store.on("clear", this.clear, this);
9484         
9485         this.el.on("contextmenu", this.onContextMenu, this);
9486         
9487         
9488         this.cm.on("headerchange", this.onHeaderChange, this);
9489         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9490
9491  //?? does bodyEl get replaced on render?
9492         this.bodyEl.on("click", this.onClick, this);
9493         this.bodyEl.on("dblclick", this.onDblClick, this);        
9494         this.bodyEl.on('scroll', this.onBodyScroll, this);
9495
9496         // guessing mainbody will work - this relays usually caught by selmodel at present.
9497         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9498   
9499   
9500         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9501         
9502   
9503         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9504             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9505         }
9506         
9507         this.initCSS();
9508     },
9509     // Compatibility with grid - we implement all the view features at present.
9510     getView : function()
9511     {
9512         return this;
9513     },
9514     
9515     initCSS : function()
9516     {
9517         if(this.disableAutoSize) {
9518             return;
9519         }
9520         
9521         var cm = this.cm, styles = [];
9522         this.CSS.removeStyleSheet(this.id + '-cssrules');
9523         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9524         // we can honour xs/sm/md/xl  as widths...
9525         // we first have to decide what widht we are currently at...
9526         var sz = Roo.getGridSize();
9527         
9528         var total = 0;
9529         var last = -1;
9530         var cols = []; // visable cols.
9531         var total_abs = 0;
9532         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9533             var w = cm.getColumnWidth(i, false);
9534             if(cm.isHidden(i)){
9535                 cols.push( { rel : false, abs : 0 });
9536                 continue;
9537             }
9538             if (w !== false) {
9539                 cols.push( { rel : false, abs : w });
9540                 total_abs += w;
9541                 last = i; // not really..
9542                 continue;
9543             }
9544             var w = cm.getColumnWidth(i, sz);
9545             if (w > 0) {
9546                 last = i
9547             }
9548             total += w;
9549             cols.push( { rel : w, abs : false });
9550         }
9551         
9552         var avail = this.bodyEl.dom.clientWidth - total_abs;
9553         
9554         var unitWidth = Math.floor(avail / total);
9555         var rem = avail - (unitWidth * total);
9556         
9557         var hidden, width, pos = 0 , splithide , left;
9558         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9559             
9560             hidden = 'display:none;';
9561             left = '';
9562             width  = 'width:0px;';
9563             splithide = '';
9564             if(!cm.isHidden(i)){
9565                 hidden = '';
9566                 
9567                 
9568                 // we can honour xs/sm/md/xl ?
9569                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9570                 if (w===0) {
9571                     hidden = 'display:none;';
9572                 }
9573                 // width should return a small number...
9574                 if (i == last) {
9575                     w+=rem; // add the remaining with..
9576                 }
9577                 pos += w;
9578                 left = "left:" + (pos -4) + "px;";
9579                 width = "width:" + w+ "px;";
9580                 
9581             }
9582             if (this.responsive) {
9583                 width = '';
9584                 left = '';
9585                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9586                 splithide = 'display: none;';
9587             }
9588             
9589             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9590             if (this.headEl) {
9591                 if (i == last) {
9592                     splithide = 'display:none;';
9593                 }
9594                 
9595                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9596                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9597                             // this is the popover version..
9598                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9599                 );
9600             }
9601             
9602         }
9603         //Roo.log(styles.join(''));
9604         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9605         
9606     },
9607     
9608     
9609     
9610     onContextMenu : function(e, t)
9611     {
9612         this.processEvent("contextmenu", e);
9613     },
9614     
9615     processEvent : function(name, e)
9616     {
9617         if (name != 'touchstart' ) {
9618             this.fireEvent(name, e);    
9619         }
9620         
9621         var t = e.getTarget();
9622         
9623         var cell = Roo.get(t);
9624         
9625         if(!cell){
9626             return;
9627         }
9628         
9629         if(cell.findParent('tfoot', false, true)){
9630             return;
9631         }
9632         
9633         if(cell.findParent('thead', false, true)){
9634             
9635             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9636                 cell = Roo.get(t).findParent('th', false, true);
9637                 if (!cell) {
9638                     Roo.log("failed to find th in thead?");
9639                     Roo.log(e.getTarget());
9640                     return;
9641                 }
9642             }
9643             
9644             var cellIndex = cell.dom.cellIndex;
9645             
9646             var ename = name == 'touchstart' ? 'click' : name;
9647             this.fireEvent("header" + ename, this, cellIndex, e);
9648             
9649             return;
9650         }
9651         
9652         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9653             cell = Roo.get(t).findParent('td', false, true);
9654             if (!cell) {
9655                 Roo.log("failed to find th in tbody?");
9656                 Roo.log(e.getTarget());
9657                 return;
9658             }
9659         }
9660         
9661         var row = cell.findParent('tr', false, true);
9662         var cellIndex = cell.dom.cellIndex;
9663         var rowIndex = row.dom.rowIndex - 1;
9664         
9665         if(row !== false){
9666             
9667             this.fireEvent("row" + name, this, rowIndex, e);
9668             
9669             if(cell !== false){
9670             
9671                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9672             }
9673         }
9674         
9675     },
9676     
9677     onMouseover : function(e, el)
9678     {
9679         var cell = Roo.get(el);
9680         
9681         if(!cell){
9682             return;
9683         }
9684         
9685         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9686             cell = cell.findParent('td', false, true);
9687         }
9688         
9689         var row = cell.findParent('tr', false, true);
9690         var cellIndex = cell.dom.cellIndex;
9691         var rowIndex = row.dom.rowIndex - 1; // start from 0
9692         
9693         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9694         
9695     },
9696     
9697     onMouseout : function(e, el)
9698     {
9699         var cell = Roo.get(el);
9700         
9701         if(!cell){
9702             return;
9703         }
9704         
9705         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9706             cell = cell.findParent('td', false, true);
9707         }
9708         
9709         var row = cell.findParent('tr', false, true);
9710         var cellIndex = cell.dom.cellIndex;
9711         var rowIndex = row.dom.rowIndex - 1; // start from 0
9712         
9713         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9714         
9715     },
9716     
9717     onClick : function(e, el)
9718     {
9719         var cell = Roo.get(el);
9720         
9721         if(!cell || (!this.cellSelection && !this.rowSelection)){
9722             return;
9723         }
9724         
9725         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9726             cell = cell.findParent('td', false, true);
9727         }
9728         
9729         if(!cell || typeof(cell) == 'undefined'){
9730             return;
9731         }
9732         
9733         var row = cell.findParent('tr', false, true);
9734         
9735         if(!row || typeof(row) == 'undefined'){
9736             return;
9737         }
9738         
9739         var cellIndex = cell.dom.cellIndex;
9740         var rowIndex = this.getRowIndex(row);
9741         
9742         // why??? - should these not be based on SelectionModel?
9743         //if(this.cellSelection){
9744             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9745         //}
9746         
9747         //if(this.rowSelection){
9748             this.fireEvent('rowclick', this, row, rowIndex, e);
9749         //}
9750          
9751     },
9752         
9753     onDblClick : function(e,el)
9754     {
9755         var cell = Roo.get(el);
9756         
9757         if(!cell || (!this.cellSelection && !this.rowSelection)){
9758             return;
9759         }
9760         
9761         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9762             cell = cell.findParent('td', false, true);
9763         }
9764         
9765         if(!cell || typeof(cell) == 'undefined'){
9766             return;
9767         }
9768         
9769         var row = cell.findParent('tr', false, true);
9770         
9771         if(!row || typeof(row) == 'undefined'){
9772             return;
9773         }
9774         
9775         var cellIndex = cell.dom.cellIndex;
9776         var rowIndex = this.getRowIndex(row);
9777         
9778         if(this.cellSelection){
9779             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9780         }
9781         
9782         if(this.rowSelection){
9783             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9784         }
9785     },
9786     findRowIndex : function(el)
9787     {
9788         var cell = Roo.get(el);
9789         if(!cell) {
9790             return false;
9791         }
9792         var row = cell.findParent('tr', false, true);
9793         
9794         if(!row || typeof(row) == 'undefined'){
9795             return false;
9796         }
9797         return this.getRowIndex(row);
9798     },
9799     sort : function(e,el)
9800     {
9801         var col = Roo.get(el);
9802         
9803         if(!col.hasClass('sortable')){
9804             return;
9805         }
9806         
9807         var sort = col.attr('sort');
9808         var dir = 'ASC';
9809         
9810         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9811             dir = 'DESC';
9812         }
9813         
9814         this.store.sortInfo = {field : sort, direction : dir};
9815         
9816         if (this.footer) {
9817             Roo.log("calling footer first");
9818             this.footer.onClick('first');
9819         } else {
9820         
9821             this.store.load({ params : { start : 0 } });
9822         }
9823     },
9824     
9825     renderHeader : function()
9826     {
9827         var header = {
9828             tag: 'thead',
9829             cn : []
9830         };
9831         
9832         var cm = this.cm;
9833         this.totalWidth = 0;
9834         
9835         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9836             
9837             var config = cm.config[i];
9838             
9839             var c = {
9840                 tag: 'th',
9841                 cls : 'x-hcol-' + i,
9842                 style : '',
9843                 
9844                 html: cm.getColumnHeader(i)
9845             };
9846             
9847             var tooltip = cm.getColumnTooltip(i);
9848             if (tooltip) {
9849                 c.tooltip = tooltip;
9850             }
9851             
9852             
9853             var hh = '';
9854             
9855             if(typeof(config.sortable) != 'undefined' && config.sortable){
9856                 c.cls += ' sortable';
9857                 c.html = '<i class="fa"></i>' + c.html;
9858             }
9859             
9860             // could use BS4 hidden-..-down 
9861             
9862             if(typeof(config.lgHeader) != 'undefined'){
9863                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9864             }
9865             
9866             if(typeof(config.mdHeader) != 'undefined'){
9867                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9868             }
9869             
9870             if(typeof(config.smHeader) != 'undefined'){
9871                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9872             }
9873             
9874             if(typeof(config.xsHeader) != 'undefined'){
9875                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9876             }
9877             
9878             if(hh.length){
9879                 c.html = hh;
9880             }
9881             
9882             if(typeof(config.tooltip) != 'undefined'){
9883                 c.tooltip = config.tooltip;
9884             }
9885             
9886             if(typeof(config.colspan) != 'undefined'){
9887                 c.colspan = config.colspan;
9888             }
9889             
9890             // hidden is handled by CSS now
9891             
9892             if(typeof(config.dataIndex) != 'undefined'){
9893                 c.sort = config.dataIndex;
9894             }
9895             
9896            
9897             
9898             if(typeof(config.align) != 'undefined' && config.align.length){
9899                 c.style += ' text-align:' + config.align + ';';
9900             }
9901             
9902             /* width is done in CSS
9903              *if(typeof(config.width) != 'undefined'){
9904                 c.style += ' width:' + config.width + 'px;';
9905                 this.totalWidth += config.width;
9906             } else {
9907                 this.totalWidth += 100; // assume minimum of 100 per column?
9908             }
9909             */
9910             
9911             if(typeof(config.cls) != 'undefined'){
9912                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9913             }
9914             // this is the bit that doesnt reall work at all...
9915             
9916             if (this.responsive) {
9917                  
9918             
9919                 ['xs','sm','md','lg'].map(function(size){
9920                     
9921                     if(typeof(config[size]) == 'undefined'){
9922                         return;
9923                     }
9924                      
9925                     if (!config[size]) { // 0 = hidden
9926                         // BS 4 '0' is treated as hide that column and below.
9927                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9928                         return;
9929                     }
9930                     
9931                     c.cls += ' col-' + size + '-' + config[size] + (
9932                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9933                     );
9934                     
9935                     
9936                 });
9937             }
9938             // at the end?
9939             
9940             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9941             
9942             
9943             
9944             
9945             header.cn.push(c)
9946         }
9947         
9948         return header;
9949     },
9950     
9951     renderBody : function()
9952     {
9953         var body = {
9954             tag: 'tbody',
9955             cn : [
9956                 {
9957                     tag: 'tr',
9958                     cn : [
9959                         {
9960                             tag : 'td',
9961                             colspan :  this.cm.getColumnCount()
9962                         }
9963                     ]
9964                 }
9965             ]
9966         };
9967         
9968         return body;
9969     },
9970     
9971     renderFooter : function()
9972     {
9973         var footer = {
9974             tag: 'tfoot',
9975             cn : [
9976                 {
9977                     tag: 'tr',
9978                     cn : [
9979                         {
9980                             tag : 'td',
9981                             colspan :  this.cm.getColumnCount()
9982                         }
9983                     ]
9984                 }
9985             ]
9986         };
9987         
9988         return footer;
9989     },
9990     
9991     onLoad : function()
9992     {
9993 //        Roo.log('ds onload');
9994         this.clear();
9995         
9996         var _this = this;
9997         var cm = this.cm;
9998         var ds = this.store;
9999         
10000         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10001             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10002             if (_this.store.sortInfo) {
10003                     
10004                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10005                     e.select('i', true).addClass(['fa-arrow-up']);
10006                 }
10007                 
10008                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10009                     e.select('i', true).addClass(['fa-arrow-down']);
10010                 }
10011             }
10012         });
10013         
10014         var tbody =  this.bodyEl;
10015               
10016         if(ds.getCount() > 0){
10017             ds.data.each(function(d,rowIndex){
10018                 var row =  this.renderRow(cm, ds, rowIndex);
10019                 
10020                 tbody.createChild(row);
10021                 
10022                 var _this = this;
10023                 
10024                 if(row.cellObjects.length){
10025                     Roo.each(row.cellObjects, function(r){
10026                         _this.renderCellObject(r);
10027                     })
10028                 }
10029                 
10030             }, this);
10031         } else if (this.empty_results.length) {
10032             this.el.mask(this.empty_results, 'no-spinner');
10033         }
10034         
10035         var tfoot = this.el.select('tfoot', true).first();
10036         
10037         if(this.footerShow && !this.footerRow && this.auto_hide_footer && this.mainFoot){
10038             
10039             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10040             
10041             var total = this.ds.getTotalCount();
10042             
10043             if(this.footer.pageSize < total){
10044                 this.mainFoot.show();
10045             }
10046         }
10047
10048         if(!this.footerShow && this.footerRow) {
10049
10050             var tr = {
10051                 tag : 'tr',
10052                 cn : []
10053             };
10054
10055             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10056                 var footer = typeof(cm.config[i].footer) == "function" ? cm.config[i].footer(ds, cm.config[i]) : cm.config[i].footer;
10057                 var td = {
10058                     tag: 'td',
10059                     cls : ' x-fcol-' + i,
10060                     html: footer
10061                 };
10062
10063                 tr.cn.push(td);
10064                 
10065             }
10066             
10067             tfoot.dom.innerHTML = '';
10068
10069             tfoot.createChild(tr);
10070         }
10071         
10072         Roo.each(this.el.select('tbody td', true).elements, function(e){
10073             e.on('mouseover', _this.onMouseover, _this);
10074         });
10075         
10076         Roo.each(this.el.select('tbody td', true).elements, function(e){
10077             e.on('mouseout', _this.onMouseout, _this);
10078         });
10079         this.fireEvent('rowsrendered', this);
10080         
10081         this.autoSize();
10082         
10083         this.initCSS(); /// resize cols
10084
10085         
10086     },
10087     
10088     
10089     onUpdate : function(ds,record)
10090     {
10091         this.refreshRow(record);
10092         this.autoSize();
10093     },
10094     
10095     onRemove : function(ds, record, index, isUpdate){
10096         if(isUpdate !== true){
10097             this.fireEvent("beforerowremoved", this, index, record);
10098         }
10099         var bt = this.bodyEl.dom;
10100         
10101         var rows = this.el.select('tbody > tr', true).elements;
10102         
10103         if(typeof(rows[index]) != 'undefined'){
10104             bt.removeChild(rows[index].dom);
10105         }
10106         
10107 //        if(bt.rows[index]){
10108 //            bt.removeChild(bt.rows[index]);
10109 //        }
10110         
10111         if(isUpdate !== true){
10112             //this.stripeRows(index);
10113             //this.syncRowHeights(index, index);
10114             //this.layout();
10115             this.fireEvent("rowremoved", this, index, record);
10116         }
10117     },
10118     
10119     onAdd : function(ds, records, rowIndex)
10120     {
10121         //Roo.log('on Add called');
10122         // - note this does not handle multiple adding very well..
10123         var bt = this.bodyEl.dom;
10124         for (var i =0 ; i < records.length;i++) {
10125             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10126             //Roo.log(records[i]);
10127             //Roo.log(this.store.getAt(rowIndex+i));
10128             this.insertRow(this.store, rowIndex + i, false);
10129             return;
10130         }
10131         
10132     },
10133     
10134     
10135     refreshRow : function(record){
10136         var ds = this.store, index;
10137         if(typeof record == 'number'){
10138             index = record;
10139             record = ds.getAt(index);
10140         }else{
10141             index = ds.indexOf(record);
10142             if (index < 0) {
10143                 return; // should not happen - but seems to 
10144             }
10145         }
10146         this.insertRow(ds, index, true);
10147         this.autoSize();
10148         this.onRemove(ds, record, index+1, true);
10149         this.autoSize();
10150         //this.syncRowHeights(index, index);
10151         //this.layout();
10152         this.fireEvent("rowupdated", this, index, record);
10153     },
10154     // private - called by RowSelection
10155     onRowSelect : function(rowIndex){
10156         var row = this.getRowDom(rowIndex);
10157         row.addClass(['bg-info','info']);
10158     },
10159     // private - called by RowSelection
10160     onRowDeselect : function(rowIndex)
10161     {
10162         if (rowIndex < 0) {
10163             return;
10164         }
10165         var row = this.getRowDom(rowIndex);
10166         row.removeClass(['bg-info','info']);
10167     },
10168       /**
10169      * Focuses the specified row.
10170      * @param {Number} row The row index
10171      */
10172     focusRow : function(row)
10173     {
10174         //Roo.log('GridView.focusRow');
10175         var x = this.bodyEl.dom.scrollLeft;
10176         this.focusCell(row, 0, false);
10177         this.bodyEl.dom.scrollLeft = x;
10178
10179     },
10180      /**
10181      * Focuses the specified cell.
10182      * @param {Number} row The row index
10183      * @param {Number} col The column index
10184      * @param {Boolean} hscroll false to disable horizontal scrolling
10185      */
10186     focusCell : function(row, col, hscroll)
10187     {
10188         //Roo.log('GridView.focusCell');
10189         var el = this.ensureVisible(row, col, hscroll);
10190         // not sure what focusEL achives = it's a <a> pos relative 
10191         //this.focusEl.alignTo(el, "tl-tl");
10192         //if(Roo.isGecko){
10193         //    this.focusEl.focus();
10194         //}else{
10195         //    this.focusEl.focus.defer(1, this.focusEl);
10196         //}
10197     },
10198     
10199      /**
10200      * Scrolls the specified cell into view
10201      * @param {Number} row The row index
10202      * @param {Number} col The column index
10203      * @param {Boolean} hscroll false to disable horizontal scrolling
10204      */
10205     ensureVisible : function(row, col, hscroll)
10206     {
10207         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10208         //return null; //disable for testing.
10209         if(typeof row != "number"){
10210             row = row.rowIndex;
10211         }
10212         if(row < 0 && row >= this.ds.getCount()){
10213             return  null;
10214         }
10215         col = (col !== undefined ? col : 0);
10216         var cm = this.cm;
10217         while(cm.isHidden(col)){
10218             col++;
10219         }
10220
10221         var el = this.getCellDom(row, col);
10222         if(!el){
10223             return null;
10224         }
10225         var c = this.bodyEl.dom;
10226
10227         var ctop = parseInt(el.offsetTop, 10);
10228         var cleft = parseInt(el.offsetLeft, 10);
10229         var cbot = ctop + el.offsetHeight;
10230         var cright = cleft + el.offsetWidth;
10231
10232         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10233         var ch = 0; //?? header is not withing the area?
10234         var stop = parseInt(c.scrollTop, 10);
10235         var sleft = parseInt(c.scrollLeft, 10);
10236         var sbot = stop + ch;
10237         var sright = sleft + c.clientWidth;
10238         /*
10239         Roo.log('GridView.ensureVisible:' +
10240                 ' ctop:' + ctop +
10241                 ' c.clientHeight:' + c.clientHeight +
10242                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10243                 ' stop:' + stop +
10244                 ' cbot:' + cbot +
10245                 ' sbot:' + sbot +
10246                 ' ch:' + ch  
10247                 );
10248         */
10249         if(ctop < stop){
10250             c.scrollTop = ctop;
10251             //Roo.log("set scrolltop to ctop DISABLE?");
10252         }else if(cbot > sbot){
10253             //Roo.log("set scrolltop to cbot-ch");
10254             c.scrollTop = cbot-ch;
10255         }
10256
10257         if(hscroll !== false){
10258             if(cleft < sleft){
10259                 c.scrollLeft = cleft;
10260             }else if(cright > sright){
10261                 c.scrollLeft = cright-c.clientWidth;
10262             }
10263         }
10264
10265         return el;
10266     },
10267     
10268     
10269     insertRow : function(dm, rowIndex, isUpdate){
10270         
10271         if(!isUpdate){
10272             this.fireEvent("beforerowsinserted", this, rowIndex);
10273         }
10274             //var s = this.getScrollState();
10275         var row = this.renderRow(this.cm, this.store, rowIndex);
10276         // insert before rowIndex..
10277         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10278         
10279         var _this = this;
10280                 
10281         if(row.cellObjects.length){
10282             Roo.each(row.cellObjects, function(r){
10283                 _this.renderCellObject(r);
10284             })
10285         }
10286             
10287         if(!isUpdate){
10288             this.fireEvent("rowsinserted", this, rowIndex);
10289             //this.syncRowHeights(firstRow, lastRow);
10290             //this.stripeRows(firstRow);
10291             //this.layout();
10292         }
10293         
10294     },
10295     
10296     
10297     getRowDom : function(rowIndex)
10298     {
10299         var rows = this.el.select('tbody > tr', true).elements;
10300         
10301         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10302         
10303     },
10304     getCellDom : function(rowIndex, colIndex)
10305     {
10306         var row = this.getRowDom(rowIndex);
10307         if (row === false) {
10308             return false;
10309         }
10310         var cols = row.select('td', true).elements;
10311         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10312         
10313     },
10314     
10315     // returns the object tree for a tr..
10316   
10317     
10318     renderRow : function(cm, ds, rowIndex) 
10319     {
10320         var d = ds.getAt(rowIndex);
10321         
10322         var row = {
10323             tag : 'tr',
10324             cls : 'x-row-' + rowIndex,
10325             cn : []
10326         };
10327             
10328         var cellObjects = [];
10329         
10330         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10331             var config = cm.config[i];
10332             
10333             var renderer = cm.getRenderer(i);
10334             var value = '';
10335             var id = false;
10336             
10337             if(typeof(renderer) !== 'undefined'){
10338                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10339             }
10340             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10341             // and are rendered into the cells after the row is rendered - using the id for the element.
10342             
10343             if(typeof(value) === 'object'){
10344                 id = Roo.id();
10345                 cellObjects.push({
10346                     container : id,
10347                     cfg : value 
10348                 })
10349             }
10350             
10351             var rowcfg = {
10352                 record: d,
10353                 rowIndex : rowIndex,
10354                 colIndex : i,
10355                 rowClass : ''
10356             };
10357
10358             this.fireEvent('rowclass', this, rowcfg);
10359             
10360             var td = {
10361                 tag: 'td',
10362                 // this might end up displaying HTML?
10363                 // this is too messy... - better to only do it on columsn you know are going to be too long
10364                 //tooltip : (typeof(value) === 'object') ? '' : value,
10365                 cls : rowcfg.rowClass + ' x-col-' + i,
10366                 style: '',
10367                 html: (typeof(value) === 'object') ? '' : value
10368             };
10369             
10370             if (id) {
10371                 td.id = id;
10372             }
10373             
10374             if(typeof(config.colspan) != 'undefined'){
10375                 td.colspan = config.colspan;
10376             }
10377             
10378             
10379             
10380             if(typeof(config.align) != 'undefined' && config.align.length){
10381                 td.style += ' text-align:' + config.align + ';';
10382             }
10383             if(typeof(config.valign) != 'undefined' && config.valign.length){
10384                 td.style += ' vertical-align:' + config.valign + ';';
10385             }
10386             /*
10387             if(typeof(config.width) != 'undefined'){
10388                 td.style += ' width:' +  config.width + 'px;';
10389             }
10390             */
10391             
10392             if(typeof(config.cursor) != 'undefined'){
10393                 td.style += ' cursor:' +  config.cursor + ';';
10394             }
10395             
10396             if(typeof(config.cls) != 'undefined'){
10397                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10398             }
10399             if (this.responsive) {
10400                 ['xs','sm','md','lg'].map(function(size){
10401                     
10402                     if(typeof(config[size]) == 'undefined'){
10403                         return;
10404                     }
10405                     
10406                     
10407                       
10408                     if (!config[size]) { // 0 = hidden
10409                         // BS 4 '0' is treated as hide that column and below.
10410                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10411                         return;
10412                     }
10413                     
10414                     td.cls += ' col-' + size + '-' + config[size] + (
10415                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10416                     );
10417                      
10418     
10419                 });
10420             }
10421             row.cn.push(td);
10422            
10423         }
10424         
10425         row.cellObjects = cellObjects;
10426         
10427         return row;
10428           
10429     },
10430     
10431     
10432     
10433     onBeforeLoad : function()
10434     {
10435         this.el.unmask(); // if needed.
10436     },
10437      /**
10438      * Remove all rows
10439      */
10440     clear : function()
10441     {
10442         this.el.select('tbody', true).first().dom.innerHTML = '';
10443     },
10444     /**
10445      * Show or hide a row.
10446      * @param {Number} rowIndex to show or hide
10447      * @param {Boolean} state hide
10448      */
10449     setRowVisibility : function(rowIndex, state)
10450     {
10451         var bt = this.bodyEl.dom;
10452         
10453         var rows = this.el.select('tbody > tr', true).elements;
10454         
10455         if(typeof(rows[rowIndex]) == 'undefined'){
10456             return;
10457         }
10458         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10459         
10460     },
10461     
10462     
10463     getSelectionModel : function(){
10464         if(!this.selModel){
10465             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10466         }
10467         return this.selModel;
10468     },
10469     /*
10470      * Render the Roo.bootstrap object from renderder
10471      */
10472     renderCellObject : function(r)
10473     {
10474         var _this = this;
10475         
10476         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10477         
10478         var t = r.cfg.render(r.container);
10479         
10480         if(r.cfg.cn){
10481             Roo.each(r.cfg.cn, function(c){
10482                 var child = {
10483                     container: t.getChildContainer(),
10484                     cfg: c
10485                 };
10486                 _this.renderCellObject(child);
10487             })
10488         }
10489     },
10490     /**
10491      * get the Row Index from a dom element.
10492      * @param {Roo.Element} row The row to look for
10493      * @returns {Number} the row
10494      */
10495     getRowIndex : function(row)
10496     {
10497         var rowIndex = -1;
10498         
10499         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10500             if(el != row){
10501                 return;
10502             }
10503             
10504             rowIndex = index;
10505         });
10506         
10507         return rowIndex;
10508     },
10509     /**
10510      * get the header TH element for columnIndex
10511      * @param {Number} columnIndex
10512      * @returns {Roo.Element}
10513      */
10514     getHeaderIndex: function(colIndex)
10515     {
10516         var cols = this.headEl.select('th', true).elements;
10517         return cols[colIndex]; 
10518     },
10519     /**
10520      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10521      * @param {domElement} cell to look for
10522      * @returns {Number} the column
10523      */
10524     getCellIndex : function(cell)
10525     {
10526         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10527         if(id){
10528             return parseInt(id[1], 10);
10529         }
10530         return 0;
10531     },
10532      /**
10533      * Returns the grid's underlying element = used by panel.Grid
10534      * @return {Element} The element
10535      */
10536     getGridEl : function(){
10537         return this.el;
10538     },
10539      /**
10540      * Forces a resize - used by panel.Grid
10541      * @return {Element} The element
10542      */
10543     autoSize : function()
10544     {
10545         if(this.disableAutoSize) {
10546             return;
10547         }
10548         //var ctr = Roo.get(this.container.dom.parentElement);
10549         var ctr = Roo.get(this.el.dom);
10550         
10551         var thd = this.getGridEl().select('thead',true).first();
10552         var tbd = this.getGridEl().select('tbody', true).first();
10553         var tfd = this.getGridEl().select('tfoot', true).first();
10554         
10555         var cw = ctr.getWidth();
10556         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10557         
10558         if (tbd) {
10559             
10560             tbd.setWidth(ctr.getWidth());
10561             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10562             // this needs fixing for various usage - currently only hydra job advers I think..
10563             //tdb.setHeight(
10564             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10565             //); 
10566             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10567             cw -= barsize;
10568         }
10569         cw = Math.max(cw, this.totalWidth);
10570         this.getGridEl().select('tbody tr',true).setWidth(cw);
10571         this.initCSS();
10572         
10573         // resize 'expandable coloumn?
10574         
10575         return; // we doe not have a view in this design..
10576         
10577     },
10578     onBodyScroll: function()
10579     {
10580         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10581         if(this.headEl){
10582             this.headEl.setStyle({
10583                 'position' : 'relative',
10584                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10585             });
10586         }
10587         
10588         if(this.lazyLoad){
10589             
10590             var scrollHeight = this.bodyEl.dom.scrollHeight;
10591             
10592             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10593             
10594             var height = this.bodyEl.getHeight();
10595             
10596             if(scrollHeight - height == scrollTop) {
10597                 
10598                 var total = this.ds.getTotalCount();
10599                 
10600                 if(this.footer.cursor + this.footer.pageSize < total){
10601                     
10602                     this.footer.ds.load({
10603                         params : {
10604                             start : this.footer.cursor + this.footer.pageSize,
10605                             limit : this.footer.pageSize
10606                         },
10607                         add : true
10608                     });
10609                 }
10610             }
10611             
10612         }
10613     },
10614     onColumnSplitterMoved : function(i, diff)
10615     {
10616         this.userResized = true;
10617         
10618         var cm = this.colModel;
10619         
10620         var w = this.getHeaderIndex(i).getWidth() + diff;
10621         
10622         
10623         cm.setColumnWidth(i, w, true);
10624         this.initCSS();
10625         //var cid = cm.getColumnId(i); << not used in this version?
10626        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10627         
10628         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10629         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10630         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10631 */
10632         //this.updateSplitters();
10633         //this.layout(); << ??
10634         this.fireEvent("columnresize", i, w);
10635     },
10636     onHeaderChange : function()
10637     {
10638         var header = this.renderHeader();
10639         var table = this.el.select('table', true).first();
10640         
10641         this.headEl.remove();
10642         this.headEl = table.createChild(header, this.bodyEl, false);
10643         
10644         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10645             e.on('click', this.sort, this);
10646         }, this);
10647         
10648         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10649             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10650         }
10651         
10652     },
10653     
10654     onHiddenChange : function(colModel, colIndex, hidden)
10655     {
10656         /*
10657         this.cm.setHidden()
10658         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10659         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10660         
10661         this.CSS.updateRule(thSelector, "display", "");
10662         this.CSS.updateRule(tdSelector, "display", "");
10663         
10664         if(hidden){
10665             this.CSS.updateRule(thSelector, "display", "none");
10666             this.CSS.updateRule(tdSelector, "display", "none");
10667         }
10668         */
10669         // onload calls initCSS()
10670         this.onHeaderChange();
10671         this.onLoad();
10672     },
10673     
10674     setColumnWidth: function(col_index, width)
10675     {
10676         // width = "md-2 xs-2..."
10677         if(!this.colModel.config[col_index]) {
10678             return;
10679         }
10680         
10681         var w = width.split(" ");
10682         
10683         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10684         
10685         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10686         
10687         
10688         for(var j = 0; j < w.length; j++) {
10689             
10690             if(!w[j]) {
10691                 continue;
10692             }
10693             
10694             var size_cls = w[j].split("-");
10695             
10696             if(!Number.isInteger(size_cls[1] * 1)) {
10697                 continue;
10698             }
10699             
10700             if(!this.colModel.config[col_index][size_cls[0]]) {
10701                 continue;
10702             }
10703             
10704             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10705                 continue;
10706             }
10707             
10708             h_row[0].classList.replace(
10709                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10710                 "col-"+size_cls[0]+"-"+size_cls[1]
10711             );
10712             
10713             for(var i = 0; i < rows.length; i++) {
10714                 
10715                 var size_cls = w[j].split("-");
10716                 
10717                 if(!Number.isInteger(size_cls[1] * 1)) {
10718                     continue;
10719                 }
10720                 
10721                 if(!this.colModel.config[col_index][size_cls[0]]) {
10722                     continue;
10723                 }
10724                 
10725                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10726                     continue;
10727                 }
10728                 
10729                 rows[i].classList.replace(
10730                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10731                     "col-"+size_cls[0]+"-"+size_cls[1]
10732                 );
10733             }
10734             
10735             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10736         }
10737     }
10738 });
10739
10740 // currently only used to find the split on drag.. 
10741 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10742
10743 /**
10744  * @depricated
10745 */
10746 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10747 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10748 /*
10749  * - LGPL
10750  *
10751  * table cell
10752  * 
10753  */
10754
10755 /**
10756  * @class Roo.bootstrap.TableCell
10757  * @extends Roo.bootstrap.Component
10758  * @children Roo.bootstrap.Component
10759  * @parent Roo.bootstrap.TableRow
10760  * Bootstrap TableCell class
10761  * 
10762  * @cfg {String} html cell contain text
10763  * @cfg {String} cls cell class
10764  * @cfg {String} tag cell tag (td|th) default td
10765  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10766  * @cfg {String} align Aligns the content in a cell
10767  * @cfg {String} axis Categorizes cells
10768  * @cfg {String} bgcolor Specifies the background color of a cell
10769  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10770  * @cfg {Number} colspan Specifies the number of columns a cell should span
10771  * @cfg {String} headers Specifies one or more header cells a cell is related to
10772  * @cfg {Number} height Sets the height of a cell
10773  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10774  * @cfg {Number} rowspan Sets the number of rows a cell should span
10775  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10776  * @cfg {String} valign Vertical aligns the content in a cell
10777  * @cfg {Number} width Specifies the width of a cell
10778  * 
10779  * @constructor
10780  * Create a new TableCell
10781  * @param {Object} config The config object
10782  */
10783
10784 Roo.bootstrap.TableCell = function(config){
10785     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10786 };
10787
10788 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10789     
10790     html: false,
10791     cls: false,
10792     tag: false,
10793     abbr: false,
10794     align: false,
10795     axis: false,
10796     bgcolor: false,
10797     charoff: false,
10798     colspan: false,
10799     headers: false,
10800     height: false,
10801     nowrap: false,
10802     rowspan: false,
10803     scope: false,
10804     valign: false,
10805     width: false,
10806     
10807     
10808     getAutoCreate : function(){
10809         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10810         
10811         cfg = {
10812             tag: 'td'
10813         };
10814         
10815         if(this.tag){
10816             cfg.tag = this.tag;
10817         }
10818         
10819         if (this.html) {
10820             cfg.html=this.html
10821         }
10822         if (this.cls) {
10823             cfg.cls=this.cls
10824         }
10825         if (this.abbr) {
10826             cfg.abbr=this.abbr
10827         }
10828         if (this.align) {
10829             cfg.align=this.align
10830         }
10831         if (this.axis) {
10832             cfg.axis=this.axis
10833         }
10834         if (this.bgcolor) {
10835             cfg.bgcolor=this.bgcolor
10836         }
10837         if (this.charoff) {
10838             cfg.charoff=this.charoff
10839         }
10840         if (this.colspan) {
10841             cfg.colspan=this.colspan
10842         }
10843         if (this.headers) {
10844             cfg.headers=this.headers
10845         }
10846         if (this.height) {
10847             cfg.height=this.height
10848         }
10849         if (this.nowrap) {
10850             cfg.nowrap=this.nowrap
10851         }
10852         if (this.rowspan) {
10853             cfg.rowspan=this.rowspan
10854         }
10855         if (this.scope) {
10856             cfg.scope=this.scope
10857         }
10858         if (this.valign) {
10859             cfg.valign=this.valign
10860         }
10861         if (this.width) {
10862             cfg.width=this.width
10863         }
10864         
10865         
10866         return cfg;
10867     }
10868    
10869 });
10870
10871  
10872
10873  /*
10874  * - LGPL
10875  *
10876  * table row
10877  * 
10878  */
10879
10880 /**
10881  * @class Roo.bootstrap.TableRow
10882  * @extends Roo.bootstrap.Component
10883  * @children Roo.bootstrap.TableCell
10884  * @parent Roo.bootstrap.TableBody
10885  * Bootstrap TableRow class
10886  * @cfg {String} cls row class
10887  * @cfg {String} align Aligns the content in a table row
10888  * @cfg {String} bgcolor Specifies a background color for a table row
10889  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10890  * @cfg {String} valign Vertical aligns the content in a table row
10891  * 
10892  * @constructor
10893  * Create a new TableRow
10894  * @param {Object} config The config object
10895  */
10896
10897 Roo.bootstrap.TableRow = function(config){
10898     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10899 };
10900
10901 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10902     
10903     cls: false,
10904     align: false,
10905     bgcolor: false,
10906     charoff: false,
10907     valign: false,
10908     
10909     getAutoCreate : function(){
10910         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10911         
10912         cfg = {
10913             tag: 'tr'
10914         };
10915             
10916         if(this.cls){
10917             cfg.cls = this.cls;
10918         }
10919         if(this.align){
10920             cfg.align = this.align;
10921         }
10922         if(this.bgcolor){
10923             cfg.bgcolor = this.bgcolor;
10924         }
10925         if(this.charoff){
10926             cfg.charoff = this.charoff;
10927         }
10928         if(this.valign){
10929             cfg.valign = this.valign;
10930         }
10931         
10932         return cfg;
10933     }
10934    
10935 });
10936
10937  
10938
10939  /*
10940  * - LGPL
10941  *
10942  * table body
10943  * 
10944  */
10945
10946 /**
10947  * @class Roo.bootstrap.TableBody
10948  * @extends Roo.bootstrap.Component
10949  * @children Roo.bootstrap.TableRow
10950  * @parent Roo.bootstrap.Table
10951  * Bootstrap TableBody class
10952  * @cfg {String} cls element class
10953  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10954  * @cfg {String} align Aligns the content inside the element
10955  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10956  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10957  * 
10958  * @constructor
10959  * Create a new TableBody
10960  * @param {Object} config The config object
10961  */
10962
10963 Roo.bootstrap.TableBody = function(config){
10964     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10965 };
10966
10967 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10968     
10969     cls: false,
10970     tag: false,
10971     align: false,
10972     charoff: false,
10973     valign: false,
10974     
10975     getAutoCreate : function(){
10976         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10977         
10978         cfg = {
10979             tag: 'tbody'
10980         };
10981             
10982         if (this.cls) {
10983             cfg.cls=this.cls
10984         }
10985         if(this.tag){
10986             cfg.tag = this.tag;
10987         }
10988         
10989         if(this.align){
10990             cfg.align = this.align;
10991         }
10992         if(this.charoff){
10993             cfg.charoff = this.charoff;
10994         }
10995         if(this.valign){
10996             cfg.valign = this.valign;
10997         }
10998         
10999         return cfg;
11000     }
11001     
11002     
11003 //    initEvents : function()
11004 //    {
11005 //        
11006 //        if(!this.store){
11007 //            return;
11008 //        }
11009 //        
11010 //        this.store = Roo.factory(this.store, Roo.data);
11011 //        this.store.on('load', this.onLoad, this);
11012 //        
11013 //        this.store.load();
11014 //        
11015 //    },
11016 //    
11017 //    onLoad: function () 
11018 //    {   
11019 //        this.fireEvent('load', this);
11020 //    }
11021 //    
11022 //   
11023 });
11024
11025  
11026
11027  /*
11028  * Based on:
11029  * Ext JS Library 1.1.1
11030  * Copyright(c) 2006-2007, Ext JS, LLC.
11031  *
11032  * Originally Released Under LGPL - original licence link has changed is not relivant.
11033  *
11034  * Fork - LGPL
11035  * <script type="text/javascript">
11036  */
11037
11038 // as we use this in bootstrap.
11039 Roo.namespace('Roo.form');
11040  /**
11041  * @class Roo.form.Action
11042  * Internal Class used to handle form actions
11043  * @constructor
11044  * @param {Roo.form.BasicForm} el The form element or its id
11045  * @param {Object} config Configuration options
11046  */
11047
11048  
11049  
11050 // define the action interface
11051 Roo.form.Action = function(form, options){
11052     this.form = form;
11053     this.options = options || {};
11054 };
11055 /**
11056  * Client Validation Failed
11057  * @const 
11058  */
11059 Roo.form.Action.CLIENT_INVALID = 'client';
11060 /**
11061  * Server Validation Failed
11062  * @const 
11063  */
11064 Roo.form.Action.SERVER_INVALID = 'server';
11065  /**
11066  * Connect to Server Failed
11067  * @const 
11068  */
11069 Roo.form.Action.CONNECT_FAILURE = 'connect';
11070 /**
11071  * Reading Data from Server Failed
11072  * @const 
11073  */
11074 Roo.form.Action.LOAD_FAILURE = 'load';
11075
11076 Roo.form.Action.prototype = {
11077     type : 'default',
11078     failureType : undefined,
11079     response : undefined,
11080     result : undefined,
11081
11082     // interface method
11083     run : function(options){
11084
11085     },
11086
11087     // interface method
11088     success : function(response){
11089
11090     },
11091
11092     // interface method
11093     handleResponse : function(response){
11094
11095     },
11096
11097     // default connection failure
11098     failure : function(response){
11099         
11100         this.response = response;
11101         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11102         this.form.afterAction(this, false);
11103     },
11104
11105     processResponse : function(response){
11106         this.response = response;
11107         if(!response.responseText){
11108             return true;
11109         }
11110         this.result = this.handleResponse(response);
11111         return this.result;
11112     },
11113
11114     // utility functions used internally
11115     getUrl : function(appendParams){
11116         var url = this.options.url || this.form.url || this.form.el.dom.action;
11117         if(appendParams){
11118             var p = this.getParams();
11119             if(p){
11120                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11121             }
11122         }
11123         return url;
11124     },
11125
11126     getMethod : function(){
11127         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11128     },
11129
11130     getParams : function(){
11131         var bp = this.form.baseParams;
11132         var p = this.options.params;
11133         if(p){
11134             if(typeof p == "object"){
11135                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11136             }else if(typeof p == 'string' && bp){
11137                 p += '&' + Roo.urlEncode(bp);
11138             }
11139         }else if(bp){
11140             p = Roo.urlEncode(bp);
11141         }
11142         return p;
11143     },
11144
11145     createCallback : function(){
11146         return {
11147             success: this.success,
11148             failure: this.failure,
11149             scope: this,
11150             timeout: (this.form.timeout*1000),
11151             upload: this.form.fileUpload ? this.success : undefined
11152         };
11153     }
11154 };
11155
11156 Roo.form.Action.Submit = function(form, options){
11157     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11158 };
11159
11160 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11161     type : 'submit',
11162
11163     haveProgress : false,
11164     uploadComplete : false,
11165     
11166     // uploadProgress indicator.
11167     uploadProgress : function()
11168     {
11169         if (!this.form.progressUrl) {
11170             return;
11171         }
11172         
11173         if (!this.haveProgress) {
11174             Roo.MessageBox.progress("Uploading", "Uploading");
11175         }
11176         if (this.uploadComplete) {
11177            Roo.MessageBox.hide();
11178            return;
11179         }
11180         
11181         this.haveProgress = true;
11182    
11183         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11184         
11185         var c = new Roo.data.Connection();
11186         c.request({
11187             url : this.form.progressUrl,
11188             params: {
11189                 id : uid
11190             },
11191             method: 'GET',
11192             success : function(req){
11193                //console.log(data);
11194                 var rdata = false;
11195                 var edata;
11196                 try  {
11197                    rdata = Roo.decode(req.responseText)
11198                 } catch (e) {
11199                     Roo.log("Invalid data from server..");
11200                     Roo.log(edata);
11201                     return;
11202                 }
11203                 if (!rdata || !rdata.success) {
11204                     Roo.log(rdata);
11205                     Roo.MessageBox.alert(Roo.encode(rdata));
11206                     return;
11207                 }
11208                 var data = rdata.data;
11209                 
11210                 if (this.uploadComplete) {
11211                    Roo.MessageBox.hide();
11212                    return;
11213                 }
11214                    
11215                 if (data){
11216                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11217                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11218                     );
11219                 }
11220                 this.uploadProgress.defer(2000,this);
11221             },
11222        
11223             failure: function(data) {
11224                 Roo.log('progress url failed ');
11225                 Roo.log(data);
11226             },
11227             scope : this
11228         });
11229            
11230     },
11231     
11232     
11233     run : function()
11234     {
11235         // run get Values on the form, so it syncs any secondary forms.
11236         this.form.getValues();
11237         
11238         var o = this.options;
11239         var method = this.getMethod();
11240         var isPost = method == 'POST';
11241         if(o.clientValidation === false || this.form.isValid()){
11242             
11243             if (this.form.progressUrl) {
11244                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11245                     (new Date() * 1) + '' + Math.random());
11246                     
11247             } 
11248             
11249             
11250             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11251                 form:this.form.el.dom,
11252                 url:this.getUrl(!isPost),
11253                 method: method,
11254                 params:isPost ? this.getParams() : null,
11255                 isUpload: this.form.fileUpload,
11256                 formData : this.form.formData
11257             }));
11258             
11259             this.uploadProgress();
11260
11261         }else if (o.clientValidation !== false){ // client validation failed
11262             this.failureType = Roo.form.Action.CLIENT_INVALID;
11263             this.form.afterAction(this, false);
11264         }
11265     },
11266
11267     success : function(response)
11268     {
11269         this.uploadComplete= true;
11270         if (this.haveProgress) {
11271             Roo.MessageBox.hide();
11272         }
11273         
11274         
11275         var result = this.processResponse(response);
11276         if(result === true || result.success){
11277             this.form.afterAction(this, true);
11278             return;
11279         }
11280         if(result.errors){
11281             this.form.markInvalid(result.errors);
11282             this.failureType = Roo.form.Action.SERVER_INVALID;
11283         }
11284         this.form.afterAction(this, false);
11285     },
11286     failure : function(response)
11287     {
11288         this.uploadComplete= true;
11289         if (this.haveProgress) {
11290             Roo.MessageBox.hide();
11291         }
11292         
11293         this.response = response;
11294         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11295         this.form.afterAction(this, false);
11296     },
11297     
11298     handleResponse : function(response){
11299         if(this.form.errorReader){
11300             var rs = this.form.errorReader.read(response);
11301             var errors = [];
11302             if(rs.records){
11303                 for(var i = 0, len = rs.records.length; i < len; i++) {
11304                     var r = rs.records[i];
11305                     errors[i] = r.data;
11306                 }
11307             }
11308             if(errors.length < 1){
11309                 errors = null;
11310             }
11311             return {
11312                 success : rs.success,
11313                 errors : errors
11314             };
11315         }
11316         var ret = false;
11317         try {
11318             var rt = response.responseText;
11319             if (rt.match(/^\<!--\[CDATA\[/)) {
11320                 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11321                 rt = rt.replace(/\]\]--\>$/,'');
11322             }
11323             
11324             ret = Roo.decode(rt);
11325         } catch (e) {
11326             ret = {
11327                 success: false,
11328                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11329                 errors : []
11330             };
11331         }
11332         return ret;
11333         
11334     }
11335 });
11336
11337
11338 Roo.form.Action.Load = function(form, options){
11339     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11340     this.reader = this.form.reader;
11341 };
11342
11343 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11344     type : 'load',
11345
11346     run : function(){
11347         
11348         Roo.Ajax.request(Roo.apply(
11349                 this.createCallback(), {
11350                     method:this.getMethod(),
11351                     url:this.getUrl(false),
11352                     params:this.getParams()
11353         }));
11354     },
11355
11356     success : function(response){
11357         
11358         var result = this.processResponse(response);
11359         if(result === true || !result.success || !result.data){
11360             this.failureType = Roo.form.Action.LOAD_FAILURE;
11361             this.form.afterAction(this, false);
11362             return;
11363         }
11364         this.form.clearInvalid();
11365         this.form.setValues(result.data);
11366         this.form.afterAction(this, true);
11367     },
11368
11369     handleResponse : function(response){
11370         if(this.form.reader){
11371             var rs = this.form.reader.read(response);
11372             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11373             return {
11374                 success : rs.success,
11375                 data : data
11376             };
11377         }
11378         return Roo.decode(response.responseText);
11379     }
11380 });
11381
11382 Roo.form.Action.ACTION_TYPES = {
11383     'load' : Roo.form.Action.Load,
11384     'submit' : Roo.form.Action.Submit
11385 };/*
11386  * - LGPL
11387  *
11388  * form
11389  *
11390  */
11391
11392 /**
11393  * @class Roo.bootstrap.form.Form
11394  * @extends Roo.bootstrap.Component
11395  * @children Roo.bootstrap.Component
11396  * Bootstrap Form class
11397  * @cfg {String} method  GET | POST (default POST)
11398  * @cfg {String} labelAlign top | left (default top)
11399  * @cfg {String} align left  | right - for navbars
11400  * @cfg {Boolean} loadMask load mask when submit (default true)
11401
11402  *
11403  * @constructor
11404  * Create a new Form
11405  * @param {Object} config The config object
11406  */
11407
11408
11409 Roo.bootstrap.form.Form = function(config){
11410     
11411     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11412     
11413     Roo.bootstrap.form.Form.popover.apply();
11414     
11415     this.addEvents({
11416         /**
11417          * @event clientvalidation
11418          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11419          * @param {Form} this
11420          * @param {Boolean} valid true if the form has passed client-side validation
11421          */
11422         clientvalidation: true,
11423         /**
11424          * @event beforeaction
11425          * Fires before any action is performed. Return false to cancel the action.
11426          * @param {Form} this
11427          * @param {Action} action The action to be performed
11428          */
11429         beforeaction: true,
11430         /**
11431          * @event actionfailed
11432          * Fires when an action fails.
11433          * @param {Form} this
11434          * @param {Action} action The action that failed
11435          */
11436         actionfailed : true,
11437         /**
11438          * @event actioncomplete
11439          * Fires when an action is completed.
11440          * @param {Form} this
11441          * @param {Action} action The action that completed
11442          */
11443         actioncomplete : true
11444     });
11445 };
11446
11447 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11448
11449      /**
11450      * @cfg {String} method
11451      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11452      */
11453     method : 'POST',
11454     /**
11455      * @cfg {String} url
11456      * The URL to use for form actions if one isn't supplied in the action options.
11457      */
11458     /**
11459      * @cfg {Boolean} fileUpload
11460      * Set to true if this form is a file upload.
11461      */
11462
11463     /**
11464      * @cfg {Object} baseParams
11465      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11466      */
11467
11468     /**
11469      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11470      */
11471     timeout: 30,
11472     /**
11473      * @cfg {Sting} align (left|right) for navbar forms
11474      */
11475     align : 'left',
11476
11477     // private
11478     activeAction : null,
11479
11480     /**
11481      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11482      * element by passing it or its id or mask the form itself by passing in true.
11483      * @type Mixed
11484      */
11485     waitMsgTarget : false,
11486
11487     loadMask : true,
11488     
11489     /**
11490      * @cfg {Boolean} errorMask (true|false) default false
11491      */
11492     errorMask : false,
11493     
11494     /**
11495      * @cfg {Number} maskOffset Default 100
11496      */
11497     maskOffset : 100,
11498     
11499     /**
11500      * @cfg {Boolean} maskBody
11501      */
11502     maskBody : false,
11503
11504     getAutoCreate : function(){
11505
11506         var cfg = {
11507             tag: 'form',
11508             method : this.method || 'POST',
11509             id : this.id || Roo.id(),
11510             cls : ''
11511         };
11512         if (this.parent().xtype.match(/^Nav/)) {
11513             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11514
11515         }
11516
11517         if (this.labelAlign == 'left' ) {
11518             cfg.cls += ' form-horizontal';
11519         }
11520
11521
11522         return cfg;
11523     },
11524     initEvents : function()
11525     {
11526         this.el.on('submit', this.onSubmit, this);
11527         // this was added as random key presses on the form where triggering form submit.
11528         this.el.on('keypress', function(e) {
11529             if (e.getCharCode() != 13) {
11530                 return true;
11531             }
11532             // we might need to allow it for textareas.. and some other items.
11533             // check e.getTarget().
11534
11535             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11536                 return true;
11537             }
11538
11539             Roo.log("keypress blocked");
11540
11541             e.preventDefault();
11542             return false;
11543         });
11544         
11545     },
11546     // private
11547     onSubmit : function(e){
11548         e.stopEvent();
11549     },
11550
11551      /**
11552      * Returns true if client-side validation on the form is successful.
11553      * @return Boolean
11554      */
11555     isValid : function(){
11556         var items = this.getItems();
11557         var valid = true;
11558         var target = false;
11559         
11560         items.each(function(f){
11561             
11562             if(f.validate()){
11563                 return;
11564             }
11565             
11566             Roo.log('invalid field: ' + f.name);
11567             
11568             valid = false;
11569
11570             if(!target && f.el.isVisible(true)){
11571                 target = f;
11572             }
11573            
11574         });
11575         
11576         if(this.errorMask && !valid){
11577             Roo.bootstrap.form.Form.popover.mask(this, target);
11578         }
11579         
11580         return valid;
11581     },
11582     
11583     /**
11584      * Returns true if any fields in this form have changed since their original load.
11585      * @return Boolean
11586      */
11587     isDirty : function(){
11588         var dirty = false;
11589         var items = this.getItems();
11590         items.each(function(f){
11591            if(f.isDirty()){
11592                dirty = true;
11593                return false;
11594            }
11595            return true;
11596         });
11597         return dirty;
11598     },
11599      /**
11600      * Performs a predefined action (submit or load) or custom actions you define on this form.
11601      * @param {String} actionName The name of the action type
11602      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11603      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11604      * accept other config options):
11605      * <pre>
11606 Property          Type             Description
11607 ----------------  ---------------  ----------------------------------------------------------------------------------
11608 url               String           The url for the action (defaults to the form's url)
11609 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11610 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11611 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11612                                    validate the form on the client (defaults to false)
11613      * </pre>
11614      * @return {BasicForm} this
11615      */
11616     doAction : function(action, options){
11617         if(typeof action == 'string'){
11618             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11619         }
11620         if(this.fireEvent('beforeaction', this, action) !== false){
11621             this.beforeAction(action);
11622             action.run.defer(100, action);
11623         }
11624         return this;
11625     },
11626
11627     // private
11628     beforeAction : function(action){
11629         var o = action.options;
11630         
11631         if(this.loadMask){
11632             
11633             if(this.maskBody){
11634                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11635             } else {
11636                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11637             }
11638         }
11639         // not really supported yet.. ??
11640
11641         //if(this.waitMsgTarget === true){
11642         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11643         //}else if(this.waitMsgTarget){
11644         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11645         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11646         //}else {
11647         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11648        // }
11649
11650     },
11651
11652     // private
11653     afterAction : function(action, success){
11654         this.activeAction = null;
11655         var o = action.options;
11656
11657         if(this.loadMask){
11658             
11659             if(this.maskBody){
11660                 Roo.get(document.body).unmask();
11661             } else {
11662                 this.el.unmask();
11663             }
11664         }
11665         
11666         //if(this.waitMsgTarget === true){
11667 //            this.el.unmask();
11668         //}else if(this.waitMsgTarget){
11669         //    this.waitMsgTarget.unmask();
11670         //}else{
11671         //    Roo.MessageBox.updateProgress(1);
11672         //    Roo.MessageBox.hide();
11673        // }
11674         //
11675         if(success){
11676             if(o.reset){
11677                 this.reset();
11678             }
11679             Roo.callback(o.success, o.scope, [this, action]);
11680             this.fireEvent('actioncomplete', this, action);
11681
11682         }else{
11683
11684             // failure condition..
11685             // we have a scenario where updates need confirming.
11686             // eg. if a locking scenario exists..
11687             // we look for { errors : { needs_confirm : true }} in the response.
11688             if (
11689                 (typeof(action.result) != 'undefined')  &&
11690                 (typeof(action.result.errors) != 'undefined')  &&
11691                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11692            ){
11693                 var _t = this;
11694                 Roo.log("not supported yet");
11695                  /*
11696
11697                 Roo.MessageBox.confirm(
11698                     "Change requires confirmation",
11699                     action.result.errorMsg,
11700                     function(r) {
11701                         if (r != 'yes') {
11702                             return;
11703                         }
11704                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11705                     }
11706
11707                 );
11708                 */
11709
11710
11711                 return;
11712             }
11713
11714             Roo.callback(o.failure, o.scope, [this, action]);
11715             // show an error message if no failed handler is set..
11716             if (!this.hasListener('actionfailed')) {
11717                 Roo.log("need to add dialog support");
11718                 /*
11719                 Roo.MessageBox.alert("Error",
11720                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11721                         action.result.errorMsg :
11722                         "Saving Failed, please check your entries or try again"
11723                 );
11724                 */
11725             }
11726
11727             this.fireEvent('actionfailed', this, action);
11728         }
11729
11730     },
11731     /**
11732      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11733      * @param {String} id The value to search for
11734      * @return Field
11735      */
11736     findField : function(id){
11737         var items = this.getItems();
11738         var field = items.get(id);
11739         if(!field){
11740              items.each(function(f){
11741                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11742                     field = f;
11743                     return false;
11744                 }
11745                 return true;
11746             });
11747         }
11748         return field || null;
11749     },
11750      /**
11751      * Mark fields in this form invalid in bulk.
11752      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11753      * @return {BasicForm} this
11754      */
11755     markInvalid : function(errors){
11756         if(errors instanceof Array){
11757             for(var i = 0, len = errors.length; i < len; i++){
11758                 var fieldError = errors[i];
11759                 var f = this.findField(fieldError.id);
11760                 if(f){
11761                     f.markInvalid(fieldError.msg);
11762                 }
11763             }
11764         }else{
11765             var field, id;
11766             for(id in errors){
11767                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11768                     field.markInvalid(errors[id]);
11769                 }
11770             }
11771         }
11772         //Roo.each(this.childForms || [], function (f) {
11773         //    f.markInvalid(errors);
11774         //});
11775
11776         return this;
11777     },
11778
11779     /**
11780      * Set values for fields in this form in bulk.
11781      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11782      * @return {BasicForm} this
11783      */
11784     setValues : function(values){
11785         if(values instanceof Array){ // array of objects
11786             for(var i = 0, len = values.length; i < len; i++){
11787                 var v = values[i];
11788                 var f = this.findField(v.id);
11789                 if(f){
11790                     f.setValue(v.value);
11791                     if(this.trackResetOnLoad){
11792                         f.originalValue = f.getValue();
11793                     }
11794                 }
11795             }
11796         }else{ // object hash
11797             var field, id;
11798             for(id in values){
11799                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11800
11801                     if (field.setFromData &&
11802                         field.valueField &&
11803                         field.displayField &&
11804                         // combos' with local stores can
11805                         // be queried via setValue()
11806                         // to set their value..
11807                         (field.store && !field.store.isLocal)
11808                         ) {
11809                         // it's a combo
11810                         var sd = { };
11811                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11812                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11813                         field.setFromData(sd);
11814
11815                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11816                         
11817                         field.setFromData(values);
11818                         
11819                     } else {
11820                         field.setValue(values[id]);
11821                     }
11822
11823
11824                     if(this.trackResetOnLoad){
11825                         field.originalValue = field.getValue();
11826                     }
11827                 }
11828             }
11829         }
11830
11831         //Roo.each(this.childForms || [], function (f) {
11832         //    f.setValues(values);
11833         //});
11834
11835         return this;
11836     },
11837
11838     /**
11839      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11840      * they are returned as an array.
11841      * @param {Boolean} asString
11842      * @return {Object}
11843      */
11844     getValues : function(asString){
11845         //if (this.childForms) {
11846             // copy values from the child forms
11847         //    Roo.each(this.childForms, function (f) {
11848         //        this.setValues(f.getValues());
11849         //    }, this);
11850         //}
11851
11852
11853
11854         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11855         if(asString === true){
11856             return fs;
11857         }
11858         return Roo.urlDecode(fs);
11859     },
11860
11861     /**
11862      * Returns the fields in this form as an object with key/value pairs.
11863      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11864      * @return {Object}
11865      */
11866     getFieldValues : function(with_hidden)
11867     {
11868         var items = this.getItems();
11869         var ret = {};
11870         items.each(function(f){
11871             
11872             if (!f.getName()) {
11873                 return;
11874             }
11875             
11876             var v = f.getValue();
11877             
11878             if (f.inputType =='radio') {
11879                 if (typeof(ret[f.getName()]) == 'undefined') {
11880                     ret[f.getName()] = ''; // empty..
11881                 }
11882
11883                 if (!f.el.dom.checked) {
11884                     return;
11885
11886                 }
11887                 v = f.el.dom.value;
11888
11889             }
11890             
11891             if(f.xtype == 'MoneyField'){
11892                 ret[f.currencyName] = f.getCurrency();
11893             }
11894
11895             // not sure if this supported any more..
11896             if ((typeof(v) == 'object') && f.getRawValue) {
11897                 v = f.getRawValue() ; // dates..
11898             }
11899             // combo boxes where name != hiddenName...
11900             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11901                 ret[f.name] = f.getRawValue();
11902             }
11903             ret[f.getName()] = v;
11904         });
11905
11906         return ret;
11907     },
11908
11909     /**
11910      * Clears all invalid messages in this form.
11911      * @return {BasicForm} this
11912      */
11913     clearInvalid : function(){
11914         var items = this.getItems();
11915
11916         items.each(function(f){
11917            f.clearInvalid();
11918         });
11919
11920         return this;
11921     },
11922
11923     /**
11924      * Resets this form.
11925      * @return {BasicForm} this
11926      */
11927     reset : function(){
11928         var items = this.getItems();
11929         items.each(function(f){
11930             f.reset();
11931         });
11932
11933         Roo.each(this.childForms || [], function (f) {
11934             f.reset();
11935         });
11936
11937
11938         return this;
11939     },
11940     
11941     getItems : function()
11942     {
11943         var r=new Roo.util.MixedCollection(false, function(o){
11944             return o.id || (o.id = Roo.id());
11945         });
11946         var iter = function(el) {
11947             if (el.inputEl) {
11948                 r.add(el);
11949             }
11950             if (!el.items) {
11951                 return;
11952             }
11953             Roo.each(el.items,function(e) {
11954                 iter(e);
11955             });
11956         };
11957
11958         iter(this);
11959         return r;
11960     },
11961     
11962     hideFields : function(items)
11963     {
11964         Roo.each(items, function(i){
11965             
11966             var f = this.findField(i);
11967             
11968             if(!f){
11969                 return;
11970             }
11971             
11972             f.hide();
11973             
11974         }, this);
11975     },
11976     
11977     showFields : function(items)
11978     {
11979         Roo.each(items, function(i){
11980             
11981             var f = this.findField(i);
11982             
11983             if(!f){
11984                 return;
11985             }
11986             
11987             f.show();
11988             
11989         }, this);
11990     }
11991
11992 });
11993
11994 Roo.apply(Roo.bootstrap.form.Form, {
11995     
11996     popover : {
11997         
11998         padding : 5,
11999         
12000         isApplied : false,
12001         
12002         isMasked : false,
12003         
12004         form : false,
12005         
12006         target : false,
12007         
12008         toolTip : false,
12009         
12010         intervalID : false,
12011         
12012         maskEl : false,
12013         
12014         apply : function()
12015         {
12016             if(this.isApplied){
12017                 return;
12018             }
12019             
12020             this.maskEl = {
12021                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12022                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12023                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12024                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12025             };
12026             
12027             this.maskEl.top.enableDisplayMode("block");
12028             this.maskEl.left.enableDisplayMode("block");
12029             this.maskEl.bottom.enableDisplayMode("block");
12030             this.maskEl.right.enableDisplayMode("block");
12031             
12032             this.toolTip = new Roo.bootstrap.Tooltip({
12033                 cls : 'roo-form-error-popover',
12034                 alignment : {
12035                     'left' : ['r-l', [-2,0], 'right'],
12036                     'right' : ['l-r', [2,0], 'left'],
12037                     'bottom' : ['tl-bl', [0,2], 'top'],
12038                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12039                 }
12040             });
12041             
12042             this.toolTip.render(Roo.get(document.body));
12043
12044             this.toolTip.el.enableDisplayMode("block");
12045             
12046             Roo.get(document.body).on('click', function(){
12047                 this.unmask();
12048             }, this);
12049             
12050             Roo.get(document.body).on('touchstart', function(){
12051                 this.unmask();
12052             }, this);
12053             
12054             this.isApplied = true
12055         },
12056         
12057         mask : function(form, target)
12058         {
12059             this.form = form;
12060             
12061             this.target = target;
12062             
12063             if(!this.form.errorMask || !target.el){
12064                 return;
12065             }
12066             
12067             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12068             
12069             Roo.log(scrollable);
12070             
12071             var ot = this.target.el.calcOffsetsTo(scrollable);
12072             
12073             var scrollTo = ot[1] - this.form.maskOffset;
12074             
12075             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12076             
12077             scrollable.scrollTo('top', scrollTo);
12078             
12079             var box = this.target.el.getBox();
12080             Roo.log(box);
12081             var zIndex = Roo.bootstrap.Modal.zIndex++;
12082
12083             
12084             this.maskEl.top.setStyle('position', 'absolute');
12085             this.maskEl.top.setStyle('z-index', zIndex);
12086             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12087             this.maskEl.top.setLeft(0);
12088             this.maskEl.top.setTop(0);
12089             this.maskEl.top.show();
12090             
12091             this.maskEl.left.setStyle('position', 'absolute');
12092             this.maskEl.left.setStyle('z-index', zIndex);
12093             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12094             this.maskEl.left.setLeft(0);
12095             this.maskEl.left.setTop(box.y - this.padding);
12096             this.maskEl.left.show();
12097
12098             this.maskEl.bottom.setStyle('position', 'absolute');
12099             this.maskEl.bottom.setStyle('z-index', zIndex);
12100             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12101             this.maskEl.bottom.setLeft(0);
12102             this.maskEl.bottom.setTop(box.bottom + this.padding);
12103             this.maskEl.bottom.show();
12104
12105             this.maskEl.right.setStyle('position', 'absolute');
12106             this.maskEl.right.setStyle('z-index', zIndex);
12107             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12108             this.maskEl.right.setLeft(box.right + this.padding);
12109             this.maskEl.right.setTop(box.y - this.padding);
12110             this.maskEl.right.show();
12111
12112             this.toolTip.bindEl = this.target.el;
12113
12114             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12115
12116             var tip = this.target.blankText;
12117
12118             if(this.target.getValue() !== '' ) {
12119                 
12120                 if (this.target.invalidText.length) {
12121                     tip = this.target.invalidText;
12122                 } else if (this.target.regexText.length){
12123                     tip = this.target.regexText;
12124                 }
12125             }
12126
12127             this.toolTip.show(tip);
12128
12129             this.intervalID = window.setInterval(function() {
12130                 Roo.bootstrap.form.Form.popover.unmask();
12131             }, 10000);
12132
12133             window.onwheel = function(){ return false;};
12134             
12135             (function(){ this.isMasked = true; }).defer(500, this);
12136             
12137         },
12138         
12139         unmask : function()
12140         {
12141             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12142                 return;
12143             }
12144             
12145             this.maskEl.top.setStyle('position', 'absolute');
12146             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12147             this.maskEl.top.hide();
12148
12149             this.maskEl.left.setStyle('position', 'absolute');
12150             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12151             this.maskEl.left.hide();
12152
12153             this.maskEl.bottom.setStyle('position', 'absolute');
12154             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12155             this.maskEl.bottom.hide();
12156
12157             this.maskEl.right.setStyle('position', 'absolute');
12158             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12159             this.maskEl.right.hide();
12160             
12161             this.toolTip.hide();
12162             
12163             this.toolTip.el.hide();
12164             
12165             window.onwheel = function(){ return true;};
12166             
12167             if(this.intervalID){
12168                 window.clearInterval(this.intervalID);
12169                 this.intervalID = false;
12170             }
12171             
12172             this.isMasked = false;
12173             
12174         }
12175         
12176     }
12177     
12178 });
12179
12180 /*
12181  * Based on:
12182  * Ext JS Library 1.1.1
12183  * Copyright(c) 2006-2007, Ext JS, LLC.
12184  *
12185  * Originally Released Under LGPL - original licence link has changed is not relivant.
12186  *
12187  * Fork - LGPL
12188  * <script type="text/javascript">
12189  */
12190 /**
12191  * @class Roo.form.VTypes
12192  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12193  * @static
12194  */
12195 Roo.form.VTypes = function(){
12196     // closure these in so they are only created once.
12197     var alpha = /^[a-zA-Z_]+$/;
12198     var alphanum = /^[a-zA-Z0-9_]+$/;
12199     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12200     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12201
12202     // All these messages and functions are configurable
12203     return {
12204         /**
12205          * The function used to validate email addresses
12206          * @param {String} value The email address
12207          */
12208         email : function(v){
12209             return email.test(v);
12210         },
12211         /**
12212          * The error text to display when the email validation function returns false
12213          * @type String
12214          */
12215         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12216         /**
12217          * The keystroke filter mask to be applied on email input
12218          * @type RegExp
12219          */
12220         emailMask : /[a-z0-9_\.\-@]/i,
12221
12222         /**
12223          * The function used to validate URLs
12224          * @param {String} value The URL
12225          */
12226         url : function(v){
12227             return url.test(v);
12228         },
12229         /**
12230          * The error text to display when the url validation function returns false
12231          * @type String
12232          */
12233         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12234         
12235         /**
12236          * The function used to validate alpha values
12237          * @param {String} value The value
12238          */
12239         alpha : function(v){
12240             return alpha.test(v);
12241         },
12242         /**
12243          * The error text to display when the alpha validation function returns false
12244          * @type String
12245          */
12246         alphaText : 'This field should only contain letters and _',
12247         /**
12248          * The keystroke filter mask to be applied on alpha input
12249          * @type RegExp
12250          */
12251         alphaMask : /[a-z_]/i,
12252
12253         /**
12254          * The function used to validate alphanumeric values
12255          * @param {String} value The value
12256          */
12257         alphanum : function(v){
12258             return alphanum.test(v);
12259         },
12260         /**
12261          * The error text to display when the alphanumeric validation function returns false
12262          * @type String
12263          */
12264         alphanumText : 'This field should only contain letters, numbers and _',
12265         /**
12266          * The keystroke filter mask to be applied on alphanumeric input
12267          * @type RegExp
12268          */
12269         alphanumMask : /[a-z0-9_]/i
12270     };
12271 }();/*
12272  * - LGPL
12273  *
12274  * Input
12275  * 
12276  */
12277
12278 /**
12279  * @class Roo.bootstrap.form.Input
12280  * @extends Roo.bootstrap.Component
12281  * Bootstrap Input class
12282  * @cfg {Boolean} disabled is it disabled
12283  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12284  * @cfg {String} name name of the input
12285  * @cfg {string} fieldLabel - the label associated
12286  * @cfg {string} placeholder - placeholder to put in text.
12287  * @cfg {string} before - input group add on before
12288  * @cfg {string} after - input group add on after
12289  * @cfg {string} size - (lg|sm) or leave empty..
12290  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12291  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12292  * @cfg {Number} md colspan out of 12 for computer-sized screens
12293  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12294  * @cfg {string} value default value of the input
12295  * @cfg {Number} labelWidth set the width of label 
12296  * @cfg {Number} labellg set the width of label (1-12)
12297  * @cfg {Number} labelmd set the width of label (1-12)
12298  * @cfg {Number} labelsm set the width of label (1-12)
12299  * @cfg {Number} labelxs set the width of label (1-12)
12300  * @cfg {String} labelAlign (top|left)
12301  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12302  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12303  * @cfg {String} indicatorpos (left|right) default left
12304  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12305  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12306  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12307  * @cfg {Roo.bootstrap.Button} before Button to show before
12308  * @cfg {Roo.bootstrap.Button} afterButton to show before
12309  * @cfg {String} align (left|center|right) Default left
12310  * @cfg {Boolean} forceFeedback (true|false) Default false
12311  * 
12312  * @constructor
12313  * Create a new Input
12314  * @param {Object} config The config object
12315  */
12316
12317 Roo.bootstrap.form.Input = function(config){
12318     
12319     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12320     
12321     this.addEvents({
12322         /**
12323          * @event focus
12324          * Fires when this field receives input focus.
12325          * @param {Roo.form.Field} this
12326          */
12327         focus : true,
12328         /**
12329          * @event blur
12330          * Fires when this field loses input focus.
12331          * @param {Roo.form.Field} this
12332          */
12333         blur : true,
12334         /**
12335          * @event specialkey
12336          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12337          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12338          * @param {Roo.form.Field} this
12339          * @param {Roo.EventObject} e The event object
12340          */
12341         specialkey : true,
12342         /**
12343          * @event change
12344          * Fires just before the field blurs if the field value has changed.
12345          * @param {Roo.form.Field} this
12346          * @param {Mixed} newValue The new value
12347          * @param {Mixed} oldValue The original value
12348          */
12349         change : true,
12350         /**
12351          * @event invalid
12352          * Fires after the field has been marked as invalid.
12353          * @param {Roo.form.Field} this
12354          * @param {String} msg The validation message
12355          */
12356         invalid : true,
12357         /**
12358          * @event valid
12359          * Fires after the field has been validated with no errors.
12360          * @param {Roo.form.Field} this
12361          */
12362         valid : true,
12363          /**
12364          * @event keyup
12365          * Fires after the key up
12366          * @param {Roo.form.Field} this
12367          * @param {Roo.EventObject}  e The event Object
12368          */
12369         keyup : true,
12370         /**
12371          * @event paste
12372          * Fires after the user pastes into input
12373          * @param {Roo.form.Field} this
12374          * @param {Roo.EventObject}  e The event Object
12375          */
12376         paste : true
12377     });
12378 };
12379
12380 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12381      /**
12382      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12383       automatic validation (defaults to "keyup").
12384      */
12385     validationEvent : "keyup",
12386      /**
12387      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12388      */
12389     validateOnBlur : true,
12390     /**
12391      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12392      */
12393     validationDelay : 250,
12394      /**
12395      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12396      */
12397     focusClass : "x-form-focus",  // not needed???
12398     
12399        
12400     /**
12401      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12402      */
12403     invalidClass : "has-warning",
12404     
12405     /**
12406      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12407      */
12408     validClass : "has-success",
12409     
12410     /**
12411      * @cfg {Boolean} hasFeedback (true|false) default true
12412      */
12413     hasFeedback : true,
12414     
12415     /**
12416      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12417      */
12418     invalidFeedbackClass : "glyphicon-warning-sign",
12419     
12420     /**
12421      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12422      */
12423     validFeedbackClass : "glyphicon-ok",
12424     
12425     /**
12426      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12427      */
12428     selectOnFocus : false,
12429     
12430      /**
12431      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12432      */
12433     maskRe : null,
12434        /**
12435      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12436      */
12437     vtype : null,
12438     
12439       /**
12440      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12441      */
12442     disableKeyFilter : false,
12443     
12444        /**
12445      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12446      */
12447     disabled : false,
12448      /**
12449      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12450      */
12451     allowBlank : true,
12452     /**
12453      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12454      */
12455     blankText : "Please complete this mandatory field",
12456     
12457      /**
12458      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12459      */
12460     minLength : 0,
12461     /**
12462      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12463      */
12464     maxLength : Number.MAX_VALUE,
12465     /**
12466      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12467      */
12468     minLengthText : "The minimum length for this field is {0}",
12469     /**
12470      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12471      */
12472     maxLengthText : "The maximum length for this field is {0}",
12473   
12474     
12475     /**
12476      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12477      * If available, this function will be called only after the basic validators all return true, and will be passed the
12478      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12479      */
12480     validator : null,
12481     /**
12482      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12483      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12484      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12485      */
12486     regex : null,
12487     /**
12488      * @cfg {String} regexText -- Depricated - use Invalid Text
12489      */
12490     regexText : "",
12491     
12492     /**
12493      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12494      */
12495     invalidText : "",
12496     
12497     
12498     
12499     autocomplete: false,
12500     
12501     
12502     fieldLabel : '',
12503     inputType : 'text',
12504     
12505     name : false,
12506     placeholder: false,
12507     before : false,
12508     after : false,
12509     size : false,
12510     hasFocus : false,
12511     preventMark: false,
12512     isFormField : true,
12513     value : '',
12514     labelWidth : 2,
12515     labelAlign : false,
12516     readOnly : false,
12517     align : false,
12518     formatedValue : false,
12519     forceFeedback : false,
12520     
12521     indicatorpos : 'left',
12522     
12523     labellg : 0,
12524     labelmd : 0,
12525     labelsm : 0,
12526     labelxs : 0,
12527     
12528     capture : '',
12529     accept : '',
12530     
12531     parentLabelAlign : function()
12532     {
12533         var parent = this;
12534         while (parent.parent()) {
12535             parent = parent.parent();
12536             if (typeof(parent.labelAlign) !='undefined') {
12537                 return parent.labelAlign;
12538             }
12539         }
12540         return 'left';
12541         
12542     },
12543     
12544     getAutoCreate : function()
12545     {
12546         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12547         
12548         var id = Roo.id();
12549         
12550         var cfg = {};
12551         
12552         if(this.inputType != 'hidden'){
12553             cfg.cls = 'form-group' //input-group
12554         }
12555         
12556         var input =  {
12557             tag: 'input',
12558             id : id,
12559             type : this.inputType,
12560             value : this.value,
12561             cls : 'form-control',
12562             placeholder : this.placeholder || '',
12563             autocomplete : this.autocomplete || 'new-password'
12564         };
12565         if (this.inputType == 'file') {
12566             input.style = 'overflow:hidden'; // why not in CSS?
12567         }
12568         
12569         if(this.capture.length){
12570             input.capture = this.capture;
12571         }
12572         
12573         if(this.accept.length){
12574             input.accept = this.accept + "/*";
12575         }
12576         
12577         if(this.align){
12578             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12579         }
12580         
12581         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12582             input.maxLength = this.maxLength;
12583         }
12584         
12585         if (this.disabled) {
12586             input.disabled=true;
12587         }
12588         
12589         if (this.readOnly) {
12590             input.readonly=true;
12591         }
12592         
12593         if (this.name) {
12594             input.name = this.name;
12595         }
12596         
12597         if (this.size) {
12598             input.cls += ' input-' + this.size;
12599         }
12600         
12601         var settings=this;
12602         ['xs','sm','md','lg'].map(function(size){
12603             if (settings[size]) {
12604                 cfg.cls += ' col-' + size + '-' + settings[size];
12605             }
12606         });
12607         
12608         var inputblock = input;
12609         
12610         var feedback = {
12611             tag: 'span',
12612             cls: 'glyphicon form-control-feedback'
12613         };
12614             
12615         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12616             
12617             inputblock = {
12618                 cls : 'has-feedback',
12619                 cn :  [
12620                     input,
12621                     feedback
12622                 ] 
12623             };  
12624         }
12625         
12626         if (this.before || this.after) {
12627             
12628             inputblock = {
12629                 cls : 'input-group',
12630                 cn :  [] 
12631             };
12632             
12633             if (this.before && typeof(this.before) == 'string') {
12634                 
12635                 inputblock.cn.push({
12636                     tag :'span',
12637                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12638                     html : this.before
12639                 });
12640             }
12641             if (this.before && typeof(this.before) == 'object') {
12642                 this.before = Roo.factory(this.before);
12643                 
12644                 inputblock.cn.push({
12645                     tag :'span',
12646                     cls : 'roo-input-before input-group-prepend   input-group-' +
12647                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12648                 });
12649             }
12650             
12651             inputblock.cn.push(input);
12652             
12653             if (this.after && typeof(this.after) == 'string') {
12654                 inputblock.cn.push({
12655                     tag :'span',
12656                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12657                     html : this.after
12658                 });
12659             }
12660             if (this.after && typeof(this.after) == 'object') {
12661                 this.after = Roo.factory(this.after);
12662                 
12663                 inputblock.cn.push({
12664                     tag :'span',
12665                     cls : 'roo-input-after input-group-append  input-group-' +
12666                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12667                 });
12668             }
12669             
12670             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12671                 inputblock.cls += ' has-feedback';
12672                 inputblock.cn.push(feedback);
12673             }
12674         };
12675         var indicator = {
12676             tag : 'i',
12677             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12678             tooltip : 'This field is required'
12679         };
12680         if (this.allowBlank ) {
12681             indicator.style = this.allowBlank ? ' display:none' : '';
12682         }
12683         if (align ==='left' && this.fieldLabel.length) {
12684             
12685             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12686             
12687             cfg.cn = [
12688                 indicator,
12689                 {
12690                     tag: 'label',
12691                     'for' :  id,
12692                     cls : 'control-label col-form-label',
12693                     html : this.fieldLabel
12694
12695                 },
12696                 {
12697                     cls : "", 
12698                     cn: [
12699                         inputblock
12700                     ]
12701                 }
12702             ];
12703             
12704             var labelCfg = cfg.cn[1];
12705             var contentCfg = cfg.cn[2];
12706             
12707             if(this.indicatorpos == 'right'){
12708                 cfg.cn = [
12709                     {
12710                         tag: 'label',
12711                         'for' :  id,
12712                         cls : 'control-label col-form-label',
12713                         cn : [
12714                             {
12715                                 tag : 'span',
12716                                 html : this.fieldLabel
12717                             },
12718                             indicator
12719                         ]
12720                     },
12721                     {
12722                         cls : "",
12723                         cn: [
12724                             inputblock
12725                         ]
12726                     }
12727
12728                 ];
12729                 
12730                 labelCfg = cfg.cn[0];
12731                 contentCfg = cfg.cn[1];
12732             
12733             }
12734             
12735             if(this.labelWidth > 12){
12736                 labelCfg.style = "width: " + this.labelWidth + 'px';
12737             }
12738             
12739             if(this.labelWidth < 13 && this.labelmd == 0){
12740                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12741             }
12742             
12743             if(this.labellg > 0){
12744                 labelCfg.cls += ' col-lg-' + this.labellg;
12745                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12746             }
12747             
12748             if(this.labelmd > 0){
12749                 labelCfg.cls += ' col-md-' + this.labelmd;
12750                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12751             }
12752             
12753             if(this.labelsm > 0){
12754                 labelCfg.cls += ' col-sm-' + this.labelsm;
12755                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12756             }
12757             
12758             if(this.labelxs > 0){
12759                 labelCfg.cls += ' col-xs-' + this.labelxs;
12760                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12761             }
12762             
12763             
12764         } else if ( this.fieldLabel.length) {
12765                 
12766             
12767             
12768             cfg.cn = [
12769                 {
12770                     tag : 'i',
12771                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12772                     tooltip : 'This field is required',
12773                     style : this.allowBlank ? ' display:none' : '' 
12774                 },
12775                 {
12776                     tag: 'label',
12777                    //cls : 'input-group-addon',
12778                     html : this.fieldLabel
12779
12780                 },
12781
12782                inputblock
12783
12784            ];
12785            
12786            if(this.indicatorpos == 'right'){
12787        
12788                 cfg.cn = [
12789                     {
12790                         tag: 'label',
12791                        //cls : 'input-group-addon',
12792                         html : this.fieldLabel
12793
12794                     },
12795                     {
12796                         tag : 'i',
12797                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12798                         tooltip : 'This field is required',
12799                         style : this.allowBlank ? ' display:none' : '' 
12800                     },
12801
12802                    inputblock
12803
12804                ];
12805
12806             }
12807
12808         } else {
12809             
12810             cfg.cn = [
12811
12812                     inputblock
12813
12814             ];
12815                 
12816                 
12817         };
12818         
12819         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12820            cfg.cls += ' navbar-form';
12821         }
12822         
12823         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12824             // on BS4 we do this only if not form 
12825             cfg.cls += ' navbar-form';
12826             cfg.tag = 'li';
12827         }
12828         
12829         return cfg;
12830         
12831     },
12832     /**
12833      * return the real input element.
12834      */
12835     inputEl: function ()
12836     {
12837         return this.el.select('input.form-control',true).first();
12838     },
12839     
12840     tooltipEl : function()
12841     {
12842         return this.inputEl();
12843     },
12844     
12845     indicatorEl : function()
12846     {
12847         if (Roo.bootstrap.version == 4) {
12848             return false; // not enabled in v4 yet.
12849         }
12850         
12851         var indicator = this.el.select('i.roo-required-indicator',true).first();
12852         
12853         if(!indicator){
12854             return false;
12855         }
12856         
12857         return indicator;
12858         
12859     },
12860     
12861     setDisabled : function(v)
12862     {
12863         var i  = this.inputEl().dom;
12864         if (!v) {
12865             i.removeAttribute('disabled');
12866             return;
12867             
12868         }
12869         i.setAttribute('disabled','true');
12870     },
12871     initEvents : function()
12872     {
12873           
12874         this.inputEl().on("keydown" , this.fireKey,  this);
12875         this.inputEl().on("focus", this.onFocus,  this);
12876         this.inputEl().on("blur", this.onBlur,  this);
12877         
12878         this.inputEl().relayEvent('keyup', this);
12879         this.inputEl().relayEvent('paste', this);
12880         
12881         this.indicator = this.indicatorEl();
12882         
12883         if(this.indicator){
12884             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12885         }
12886  
12887         // reference to original value for reset
12888         this.originalValue = this.getValue();
12889         //Roo.form.TextField.superclass.initEvents.call(this);
12890         if(this.validationEvent == 'keyup'){
12891             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12892             this.inputEl().on('keyup', this.filterValidation, this);
12893         }
12894         else if(this.validationEvent !== false){
12895             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12896         }
12897         
12898         if(this.selectOnFocus){
12899             this.on("focus", this.preFocus, this);
12900             
12901         }
12902         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12903             this.inputEl().on("keypress", this.filterKeys, this);
12904         } else {
12905             this.inputEl().relayEvent('keypress', this);
12906         }
12907        /* if(this.grow){
12908             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12909             this.el.on("click", this.autoSize,  this);
12910         }
12911         */
12912         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12913             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12914         }
12915         
12916         if (typeof(this.before) == 'object') {
12917             this.before.render(this.el.select('.roo-input-before',true).first());
12918         }
12919         if (typeof(this.after) == 'object') {
12920             this.after.render(this.el.select('.roo-input-after',true).first());
12921         }
12922         
12923         this.inputEl().on('change', this.onChange, this);
12924         
12925     },
12926     filterValidation : function(e){
12927         if(!e.isNavKeyPress()){
12928             this.validationTask.delay(this.validationDelay);
12929         }
12930     },
12931      /**
12932      * Validates the field value
12933      * @return {Boolean} True if the value is valid, else false
12934      */
12935     validate : function(){
12936         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12937         if(this.disabled || this.validateValue(this.getRawValue())){
12938             this.markValid();
12939             return true;
12940         }
12941         
12942         this.markInvalid();
12943         return false;
12944     },
12945     
12946     
12947     /**
12948      * Validates a value according to the field's validation rules and marks the field as invalid
12949      * if the validation fails
12950      * @param {Mixed} value The value to validate
12951      * @return {Boolean} True if the value is valid, else false
12952      */
12953     validateValue : function(value)
12954     {
12955         if(this.getVisibilityEl().hasClass('hidden')){
12956             return true;
12957         }
12958         
12959         if(value.length < 1)  { // if it's blank
12960             if(this.allowBlank){
12961                 return true;
12962             }
12963             return false;
12964         }
12965         
12966         if(value.length < this.minLength){
12967             return false;
12968         }
12969         if(value.length > this.maxLength){
12970             return false;
12971         }
12972         if(this.vtype){
12973             var vt = Roo.form.VTypes;
12974             if(!vt[this.vtype](value, this)){
12975                 return false;
12976             }
12977         }
12978         if(typeof this.validator == "function"){
12979             var msg = this.validator(value);
12980             if (typeof(msg) == 'string') {
12981                 this.invalidText = msg;
12982             }
12983             if(msg !== true){
12984                 return false;
12985             }
12986         }
12987         
12988         if(this.regex && !this.regex.test(value)){
12989             return false;
12990         }
12991         
12992         return true;
12993     },
12994     
12995      // private
12996     fireKey : function(e){
12997         //Roo.log('field ' + e.getKey());
12998         if(e.isNavKeyPress()){
12999             this.fireEvent("specialkey", this, e);
13000         }
13001     },
13002     focus : function (selectText){
13003         if(this.rendered){
13004             this.inputEl().focus();
13005             if(selectText === true){
13006                 this.inputEl().dom.select();
13007             }
13008         }
13009         return this;
13010     } ,
13011     
13012     onFocus : function(){
13013         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13014            // this.el.addClass(this.focusClass);
13015         }
13016         if(!this.hasFocus){
13017             this.hasFocus = true;
13018             this.startValue = this.getValue();
13019             this.fireEvent("focus", this);
13020         }
13021     },
13022     
13023     beforeBlur : Roo.emptyFn,
13024
13025     
13026     // private
13027     onBlur : function(){
13028         this.beforeBlur();
13029         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13030             //this.el.removeClass(this.focusClass);
13031         }
13032         this.hasFocus = false;
13033         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13034             this.validate();
13035         }
13036         var v = this.getValue();
13037         if(String(v) !== String(this.startValue)){
13038             this.fireEvent('change', this, v, this.startValue);
13039         }
13040         this.fireEvent("blur", this);
13041     },
13042     
13043     onChange : function(e)
13044     {
13045         var v = this.getValue();
13046         if(String(v) !== String(this.startValue)){
13047             this.fireEvent('change', this, v, this.startValue);
13048         }
13049         
13050     },
13051     
13052     /**
13053      * Resets the current field value to the originally loaded value and clears any validation messages
13054      */
13055     reset : function(){
13056         this.setValue(this.originalValue);
13057         this.validate();
13058     },
13059      /**
13060      * Returns the name of the field
13061      * @return {Mixed} name The name field
13062      */
13063     getName: function(){
13064         return this.name;
13065     },
13066      /**
13067      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13068      * @return {Mixed} value The field value
13069      */
13070     getValue : function(){
13071         
13072         var v = this.inputEl().getValue();
13073         
13074         return v;
13075     },
13076     /**
13077      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13078      * @return {Mixed} value The field value
13079      */
13080     getRawValue : function(){
13081         var v = this.inputEl().getValue();
13082         
13083         return v;
13084     },
13085     
13086     /**
13087      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13088      * @param {Mixed} value The value to set
13089      */
13090     setRawValue : function(v){
13091         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13092     },
13093     
13094     selectText : function(start, end){
13095         var v = this.getRawValue();
13096         if(v.length > 0){
13097             start = start === undefined ? 0 : start;
13098             end = end === undefined ? v.length : end;
13099             var d = this.inputEl().dom;
13100             if(d.setSelectionRange){
13101                 d.setSelectionRange(start, end);
13102             }else if(d.createTextRange){
13103                 var range = d.createTextRange();
13104                 range.moveStart("character", start);
13105                 range.moveEnd("character", v.length-end);
13106                 range.select();
13107             }
13108         }
13109     },
13110     
13111     /**
13112      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13113      * @param {Mixed} value The value to set
13114      */
13115     setValue : function(v){
13116         this.value = v;
13117         if(this.rendered){
13118             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13119             this.validate();
13120         }
13121     },
13122     
13123     /*
13124     processValue : function(value){
13125         if(this.stripCharsRe){
13126             var newValue = value.replace(this.stripCharsRe, '');
13127             if(newValue !== value){
13128                 this.setRawValue(newValue);
13129                 return newValue;
13130             }
13131         }
13132         return value;
13133     },
13134   */
13135     preFocus : function(){
13136         
13137         if(this.selectOnFocus){
13138             this.inputEl().dom.select();
13139         }
13140     },
13141     filterKeys : function(e){
13142         var k = e.getKey();
13143         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13144             return;
13145         }
13146         var c = e.getCharCode(), cc = String.fromCharCode(c);
13147         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13148             return;
13149         }
13150         if(!this.maskRe.test(cc)){
13151             e.stopEvent();
13152         }
13153     },
13154      /**
13155      * Clear any invalid styles/messages for this field
13156      */
13157     clearInvalid : function(){
13158         
13159         if(!this.el || this.preventMark){ // not rendered
13160             return;
13161         }
13162         
13163         
13164         this.el.removeClass([this.invalidClass, 'is-invalid']);
13165         
13166         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13167             
13168             var feedback = this.el.select('.form-control-feedback', true).first();
13169             
13170             if(feedback){
13171                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13172             }
13173             
13174         }
13175         
13176         if(this.indicator){
13177             this.indicator.removeClass('visible');
13178             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13179         }
13180         
13181         this.fireEvent('valid', this);
13182     },
13183     
13184      /**
13185      * Mark this field as valid
13186      */
13187     markValid : function()
13188     {
13189         if(!this.el  || this.preventMark){ // not rendered...
13190             return;
13191         }
13192         
13193         this.el.removeClass([this.invalidClass, this.validClass]);
13194         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13195
13196         var feedback = this.el.select('.form-control-feedback', true).first();
13197             
13198         if(feedback){
13199             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13200         }
13201         
13202         if(this.indicator){
13203             this.indicator.removeClass('visible');
13204             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13205         }
13206         
13207         if(this.disabled){
13208             return;
13209         }
13210         
13211            
13212         if(this.allowBlank && !this.getRawValue().length){
13213             return;
13214         }
13215         if (Roo.bootstrap.version == 3) {
13216             this.el.addClass(this.validClass);
13217         } else {
13218             this.inputEl().addClass('is-valid');
13219         }
13220
13221         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13222             
13223             var feedback = this.el.select('.form-control-feedback', true).first();
13224             
13225             if(feedback){
13226                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13227                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13228             }
13229             
13230         }
13231         
13232         this.fireEvent('valid', this);
13233     },
13234     
13235      /**
13236      * Mark this field as invalid
13237      * @param {String} msg The validation message
13238      */
13239     markInvalid : function(msg)
13240     {
13241         if(!this.el  || this.preventMark){ // not rendered
13242             return;
13243         }
13244         
13245         this.el.removeClass([this.invalidClass, this.validClass]);
13246         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13247         
13248         var feedback = this.el.select('.form-control-feedback', true).first();
13249             
13250         if(feedback){
13251             this.el.select('.form-control-feedback', true).first().removeClass(
13252                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13253         }
13254
13255         if(this.disabled){
13256             return;
13257         }
13258         
13259         if(this.allowBlank && !this.getRawValue().length){
13260             return;
13261         }
13262         
13263         if(this.indicator){
13264             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13265             this.indicator.addClass('visible');
13266         }
13267         if (Roo.bootstrap.version == 3) {
13268             this.el.addClass(this.invalidClass);
13269         } else {
13270             this.inputEl().addClass('is-invalid');
13271         }
13272         
13273         
13274         
13275         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13276             
13277             var feedback = this.el.select('.form-control-feedback', true).first();
13278             
13279             if(feedback){
13280                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13281                 
13282                 if(this.getValue().length || this.forceFeedback){
13283                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13284                 }
13285                 
13286             }
13287             
13288         }
13289         
13290         this.fireEvent('invalid', this, msg);
13291     },
13292     // private
13293     SafariOnKeyDown : function(event)
13294     {
13295         // this is a workaround for a password hang bug on chrome/ webkit.
13296         if (this.inputEl().dom.type != 'password') {
13297             return;
13298         }
13299         
13300         var isSelectAll = false;
13301         
13302         if(this.inputEl().dom.selectionEnd > 0){
13303             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13304         }
13305         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13306             event.preventDefault();
13307             this.setValue('');
13308             return;
13309         }
13310         
13311         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13312             
13313             event.preventDefault();
13314             // this is very hacky as keydown always get's upper case.
13315             //
13316             var cc = String.fromCharCode(event.getCharCode());
13317             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13318             
13319         }
13320     },
13321     adjustWidth : function(tag, w){
13322         tag = tag.toLowerCase();
13323         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13324             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13325                 if(tag == 'input'){
13326                     return w + 2;
13327                 }
13328                 if(tag == 'textarea'){
13329                     return w-2;
13330                 }
13331             }else if(Roo.isOpera){
13332                 if(tag == 'input'){
13333                     return w + 2;
13334                 }
13335                 if(tag == 'textarea'){
13336                     return w-2;
13337                 }
13338             }
13339         }
13340         return w;
13341     },
13342     
13343     setFieldLabel : function(v)
13344     {
13345         if(!this.rendered){
13346             return;
13347         }
13348         
13349         if(this.indicatorEl()){
13350             var ar = this.el.select('label > span',true);
13351             
13352             if (ar.elements.length) {
13353                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13354                 this.fieldLabel = v;
13355                 return;
13356             }
13357             
13358             var br = this.el.select('label',true);
13359             
13360             if(br.elements.length) {
13361                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13362                 this.fieldLabel = v;
13363                 return;
13364             }
13365             
13366             Roo.log('Cannot Found any of label > span || label in input');
13367             return;
13368         }
13369         
13370         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13371         this.fieldLabel = v;
13372         
13373         
13374     }
13375 });
13376
13377  
13378 /*
13379  * - LGPL
13380  *
13381  * Input
13382  * 
13383  */
13384
13385 /**
13386  * @class Roo.bootstrap.form.TextArea
13387  * @extends Roo.bootstrap.form.Input
13388  * Bootstrap TextArea class
13389  * @cfg {Number} cols Specifies the visible width of a text area
13390  * @cfg {Number} rows Specifies the visible number of lines in a text area
13391  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13392  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13393  * @cfg {string} html text
13394  * 
13395  * @constructor
13396  * Create a new TextArea
13397  * @param {Object} config The config object
13398  */
13399
13400 Roo.bootstrap.form.TextArea = function(config){
13401     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13402    
13403 };
13404
13405 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13406      
13407     cols : false,
13408     rows : 5,
13409     readOnly : false,
13410     warp : 'soft',
13411     resize : false,
13412     value: false,
13413     html: false,
13414     
13415     getAutoCreate : function(){
13416         
13417         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13418         
13419         var id = Roo.id();
13420         
13421         var cfg = {};
13422         
13423         if(this.inputType != 'hidden'){
13424             cfg.cls = 'form-group' //input-group
13425         }
13426         
13427         var input =  {
13428             tag: 'textarea',
13429             id : id,
13430             warp : this.warp,
13431             rows : this.rows,
13432             value : this.value || '',
13433             html: this.html || '',
13434             cls : 'form-control',
13435             placeholder : this.placeholder || '' 
13436             
13437         };
13438         
13439         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13440             input.maxLength = this.maxLength;
13441         }
13442         
13443         if(this.resize){
13444             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13445         }
13446         
13447         if(this.cols){
13448             input.cols = this.cols;
13449         }
13450         
13451         if (this.readOnly) {
13452             input.readonly = true;
13453         }
13454         
13455         if (this.name) {
13456             input.name = this.name;
13457         }
13458         
13459         if (this.size) {
13460             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13461         }
13462         
13463         var settings=this;
13464         ['xs','sm','md','lg'].map(function(size){
13465             if (settings[size]) {
13466                 cfg.cls += ' col-' + size + '-' + settings[size];
13467             }
13468         });
13469         
13470         var inputblock = input;
13471         
13472         if(this.hasFeedback && !this.allowBlank){
13473             
13474             var feedback = {
13475                 tag: 'span',
13476                 cls: 'glyphicon form-control-feedback'
13477             };
13478
13479             inputblock = {
13480                 cls : 'has-feedback',
13481                 cn :  [
13482                     input,
13483                     feedback
13484                 ] 
13485             };  
13486         }
13487         
13488         
13489         if (this.before || this.after) {
13490             
13491             inputblock = {
13492                 cls : 'input-group',
13493                 cn :  [] 
13494             };
13495             if (this.before) {
13496                 inputblock.cn.push({
13497                     tag :'span',
13498                     cls : 'input-group-addon',
13499                     html : this.before
13500                 });
13501             }
13502             
13503             inputblock.cn.push(input);
13504             
13505             if(this.hasFeedback && !this.allowBlank){
13506                 inputblock.cls += ' has-feedback';
13507                 inputblock.cn.push(feedback);
13508             }
13509             
13510             if (this.after) {
13511                 inputblock.cn.push({
13512                     tag :'span',
13513                     cls : 'input-group-addon',
13514                     html : this.after
13515                 });
13516             }
13517             
13518         }
13519         
13520         if (align ==='left' && this.fieldLabel.length) {
13521             cfg.cn = [
13522                 {
13523                     tag: 'label',
13524                     'for' :  id,
13525                     cls : 'control-label',
13526                     html : this.fieldLabel
13527                 },
13528                 {
13529                     cls : "",
13530                     cn: [
13531                         inputblock
13532                     ]
13533                 }
13534
13535             ];
13536             
13537             if(this.labelWidth > 12){
13538                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13539             }
13540
13541             if(this.labelWidth < 13 && this.labelmd == 0){
13542                 this.labelmd = this.labelWidth;
13543             }
13544
13545             if(this.labellg > 0){
13546                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13547                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13548             }
13549
13550             if(this.labelmd > 0){
13551                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13552                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13553             }
13554
13555             if(this.labelsm > 0){
13556                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13557                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13558             }
13559
13560             if(this.labelxs > 0){
13561                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13562                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13563             }
13564             
13565         } else if ( this.fieldLabel.length) {
13566             cfg.cn = [
13567
13568                {
13569                    tag: 'label',
13570                    //cls : 'input-group-addon',
13571                    html : this.fieldLabel
13572
13573                },
13574
13575                inputblock
13576
13577            ];
13578
13579         } else {
13580
13581             cfg.cn = [
13582
13583                 inputblock
13584
13585             ];
13586                 
13587         }
13588         
13589         if (this.disabled) {
13590             input.disabled=true;
13591         }
13592         
13593         return cfg;
13594         
13595     },
13596     /**
13597      * return the real textarea element.
13598      */
13599     inputEl: function ()
13600     {
13601         return this.el.select('textarea.form-control',true).first();
13602     },
13603     
13604     /**
13605      * Clear any invalid styles/messages for this field
13606      */
13607     clearInvalid : function()
13608     {
13609         
13610         if(!this.el || this.preventMark){ // not rendered
13611             return;
13612         }
13613         
13614         var label = this.el.select('label', true).first();
13615         var icon = this.el.select('i.fa-star', true).first();
13616         
13617         if(label && icon){
13618             icon.remove();
13619         }
13620         this.el.removeClass( this.validClass);
13621         this.inputEl().removeClass('is-invalid');
13622          
13623         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13624             
13625             var feedback = this.el.select('.form-control-feedback', true).first();
13626             
13627             if(feedback){
13628                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13629             }
13630             
13631         }
13632         
13633         this.fireEvent('valid', this);
13634     },
13635     
13636      /**
13637      * Mark this field as valid
13638      */
13639     markValid : function()
13640     {
13641         if(!this.el  || this.preventMark){ // not rendered
13642             return;
13643         }
13644         
13645         this.el.removeClass([this.invalidClass, this.validClass]);
13646         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13647         
13648         var feedback = this.el.select('.form-control-feedback', true).first();
13649             
13650         if(feedback){
13651             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13652         }
13653
13654         if(this.disabled || this.allowBlank){
13655             return;
13656         }
13657         
13658         var label = this.el.select('label', true).first();
13659         var icon = this.el.select('i.fa-star', true).first();
13660         
13661         if(label && icon){
13662             icon.remove();
13663         }
13664         if (Roo.bootstrap.version == 3) {
13665             this.el.addClass(this.validClass);
13666         } else {
13667             this.inputEl().addClass('is-valid');
13668         }
13669         
13670         
13671         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13672             
13673             var feedback = this.el.select('.form-control-feedback', true).first();
13674             
13675             if(feedback){
13676                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13677                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13678             }
13679             
13680         }
13681         
13682         this.fireEvent('valid', this);
13683     },
13684     
13685      /**
13686      * Mark this field as invalid
13687      * @param {String} msg The validation message
13688      */
13689     markInvalid : function(msg)
13690     {
13691         if(!this.el  || this.preventMark){ // not rendered
13692             return;
13693         }
13694         
13695         this.el.removeClass([this.invalidClass, this.validClass]);
13696         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13697         
13698         var feedback = this.el.select('.form-control-feedback', true).first();
13699             
13700         if(feedback){
13701             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13702         }
13703
13704         if(this.disabled || this.allowBlank){
13705             return;
13706         }
13707         
13708         var label = this.el.select('label', true).first();
13709         var icon = this.el.select('i.fa-star', true).first();
13710         
13711         if(!this.getValue().length && label && !icon){
13712             this.el.createChild({
13713                 tag : 'i',
13714                 cls : 'text-danger fa fa-lg fa-star',
13715                 tooltip : 'This field is required',
13716                 style : 'margin-right:5px;'
13717             }, label, true);
13718         }
13719         
13720         if (Roo.bootstrap.version == 3) {
13721             this.el.addClass(this.invalidClass);
13722         } else {
13723             this.inputEl().addClass('is-invalid');
13724         }
13725         
13726         // fixme ... this may be depricated need to test..
13727         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13728             
13729             var feedback = this.el.select('.form-control-feedback', true).first();
13730             
13731             if(feedback){
13732                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13733                 
13734                 if(this.getValue().length || this.forceFeedback){
13735                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13736                 }
13737                 
13738             }
13739             
13740         }
13741         
13742         this.fireEvent('invalid', this, msg);
13743     }
13744 });
13745
13746  
13747 /*
13748  * - LGPL
13749  *
13750  * trigger field - base class for combo..
13751  * 
13752  */
13753  
13754 /**
13755  * @class Roo.bootstrap.form.TriggerField
13756  * @extends Roo.bootstrap.form.Input
13757  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13758  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13759  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13760  * for which you can provide a custom implementation.  For example:
13761  * <pre><code>
13762 var trigger = new Roo.bootstrap.form.TriggerField();
13763 trigger.onTriggerClick = myTriggerFn;
13764 trigger.applyTo('my-field');
13765 </code></pre>
13766  *
13767  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13768  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13769  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13770  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13771  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13772
13773  * @constructor
13774  * Create a new TriggerField.
13775  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13776  * to the base TextField)
13777  */
13778 Roo.bootstrap.form.TriggerField = function(config){
13779     this.mimicing = false;
13780     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13781 };
13782
13783 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13784     /**
13785      * @cfg {String} triggerClass A CSS class to apply to the trigger
13786      */
13787      /**
13788      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13789      */
13790     hideTrigger:false,
13791
13792     /**
13793      * @cfg {Boolean} removable (true|false) special filter default false
13794      */
13795     removable : false,
13796     
13797     /** @cfg {Boolean} grow @hide */
13798     /** @cfg {Number} growMin @hide */
13799     /** @cfg {Number} growMax @hide */
13800
13801     /**
13802      * @hide 
13803      * @method
13804      */
13805     autoSize: Roo.emptyFn,
13806     // private
13807     monitorTab : true,
13808     // private
13809     deferHeight : true,
13810
13811     
13812     actionMode : 'wrap',
13813     
13814     caret : false,
13815     
13816     
13817     getAutoCreate : function(){
13818        
13819         var align = this.labelAlign || this.parentLabelAlign();
13820         
13821         var id = Roo.id();
13822         
13823         var cfg = {
13824             cls: 'form-group' //input-group
13825         };
13826         
13827         
13828         var input =  {
13829             tag: 'input',
13830             id : id,
13831             type : this.inputType,
13832             cls : 'form-control',
13833             autocomplete: 'new-password',
13834             placeholder : this.placeholder || '' 
13835             
13836         };
13837         if (this.name) {
13838             input.name = this.name;
13839         }
13840         if (this.size) {
13841             input.cls += ' input-' + this.size;
13842         }
13843         
13844         if (this.disabled) {
13845             input.disabled=true;
13846         }
13847         
13848         var inputblock = input;
13849         
13850         if(this.hasFeedback && !this.allowBlank){
13851             
13852             var feedback = {
13853                 tag: 'span',
13854                 cls: 'glyphicon form-control-feedback'
13855             };
13856             
13857             if(this.removable && !this.editable  ){
13858                 inputblock = {
13859                     cls : 'has-feedback',
13860                     cn :  [
13861                         inputblock,
13862                         {
13863                             tag: 'button',
13864                             html : 'x',
13865                             cls : 'roo-combo-removable-btn close'
13866                         },
13867                         feedback
13868                     ] 
13869                 };
13870             } else {
13871                 inputblock = {
13872                     cls : 'has-feedback',
13873                     cn :  [
13874                         inputblock,
13875                         feedback
13876                     ] 
13877                 };
13878             }
13879
13880         } else {
13881             if(this.removable && !this.editable ){
13882                 inputblock = {
13883                     cls : 'roo-removable',
13884                     cn :  [
13885                         inputblock,
13886                         {
13887                             tag: 'button',
13888                             html : 'x',
13889                             cls : 'roo-combo-removable-btn close'
13890                         }
13891                     ] 
13892                 };
13893             }
13894         }
13895         
13896         if (this.before || this.after) {
13897             
13898             inputblock = {
13899                 cls : 'input-group',
13900                 cn :  [] 
13901             };
13902             if (this.before) {
13903                 inputblock.cn.push({
13904                     tag :'span',
13905                     cls : 'input-group-addon input-group-prepend input-group-text',
13906                     html : this.before
13907                 });
13908             }
13909             
13910             inputblock.cn.push(input);
13911             
13912             if(this.hasFeedback && !this.allowBlank){
13913                 inputblock.cls += ' has-feedback';
13914                 inputblock.cn.push(feedback);
13915             }
13916             
13917             if (this.after) {
13918                 inputblock.cn.push({
13919                     tag :'span',
13920                     cls : 'input-group-addon input-group-append input-group-text',
13921                     html : this.after
13922                 });
13923             }
13924             
13925         };
13926         
13927       
13928         
13929         var ibwrap = inputblock;
13930         
13931         if(this.multiple){
13932             ibwrap = {
13933                 tag: 'ul',
13934                 cls: 'roo-select2-choices',
13935                 cn:[
13936                     {
13937                         tag: 'li',
13938                         cls: 'roo-select2-search-field',
13939                         cn: [
13940
13941                             inputblock
13942                         ]
13943                     }
13944                 ]
13945             };
13946                 
13947         }
13948         
13949         var combobox = {
13950             cls: 'roo-select2-container input-group',
13951             cn: [
13952                  {
13953                     tag: 'input',
13954                     type : 'hidden',
13955                     cls: 'form-hidden-field'
13956                 },
13957                 ibwrap
13958             ]
13959         };
13960         
13961         if(!this.multiple && this.showToggleBtn){
13962             
13963             var caret = {
13964                         tag: 'span',
13965                         cls: 'caret'
13966              };
13967             if (this.caret != false) {
13968                 caret = {
13969                      tag: 'i',
13970                      cls: 'fa fa-' + this.caret
13971                 };
13972                 
13973             }
13974             
13975             combobox.cn.push({
13976                 tag :'span',
13977                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13978                 cn : [
13979                     Roo.bootstrap.version == 3 ? caret : '',
13980                     {
13981                         tag: 'span',
13982                         cls: 'combobox-clear',
13983                         cn  : [
13984                             {
13985                                 tag : 'i',
13986                                 cls: 'icon-remove'
13987                             }
13988                         ]
13989                     }
13990                 ]
13991
13992             })
13993         }
13994         
13995         if(this.multiple){
13996             combobox.cls += ' roo-select2-container-multi';
13997         }
13998          var indicator = {
13999             tag : 'i',
14000             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14001             tooltip : 'This field is required'
14002         };
14003         if (Roo.bootstrap.version == 4) {
14004             indicator = {
14005                 tag : 'i',
14006                 style : 'display:none'
14007             };
14008         }
14009         
14010         
14011         if (align ==='left' && this.fieldLabel.length) {
14012             
14013             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14014
14015             cfg.cn = [
14016                 indicator,
14017                 {
14018                     tag: 'label',
14019                     'for' :  id,
14020                     cls : 'control-label',
14021                     html : this.fieldLabel
14022
14023                 },
14024                 {
14025                     cls : "", 
14026                     cn: [
14027                         combobox
14028                     ]
14029                 }
14030
14031             ];
14032             
14033             var labelCfg = cfg.cn[1];
14034             var contentCfg = cfg.cn[2];
14035             
14036             if(this.indicatorpos == 'right'){
14037                 cfg.cn = [
14038                     {
14039                         tag: 'label',
14040                         'for' :  id,
14041                         cls : 'control-label',
14042                         cn : [
14043                             {
14044                                 tag : 'span',
14045                                 html : this.fieldLabel
14046                             },
14047                             indicator
14048                         ]
14049                     },
14050                     {
14051                         cls : "", 
14052                         cn: [
14053                             combobox
14054                         ]
14055                     }
14056
14057                 ];
14058                 
14059                 labelCfg = cfg.cn[0];
14060                 contentCfg = cfg.cn[1];
14061             }
14062             
14063             if(this.labelWidth > 12){
14064                 labelCfg.style = "width: " + this.labelWidth + 'px';
14065             }
14066             
14067             if(this.labelWidth < 13 && this.labelmd == 0){
14068                 this.labelmd = this.labelWidth;
14069             }
14070             
14071             if(this.labellg > 0){
14072                 labelCfg.cls += ' col-lg-' + this.labellg;
14073                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14074             }
14075             
14076             if(this.labelmd > 0){
14077                 labelCfg.cls += ' col-md-' + this.labelmd;
14078                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14079             }
14080             
14081             if(this.labelsm > 0){
14082                 labelCfg.cls += ' col-sm-' + this.labelsm;
14083                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14084             }
14085             
14086             if(this.labelxs > 0){
14087                 labelCfg.cls += ' col-xs-' + this.labelxs;
14088                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14089             }
14090             
14091         } else if ( this.fieldLabel.length) {
14092 //                Roo.log(" label");
14093             cfg.cn = [
14094                 indicator,
14095                {
14096                    tag: 'label',
14097                    //cls : 'input-group-addon',
14098                    html : this.fieldLabel
14099
14100                },
14101
14102                combobox
14103
14104             ];
14105             
14106             if(this.indicatorpos == 'right'){
14107                 
14108                 cfg.cn = [
14109                     {
14110                        tag: 'label',
14111                        cn : [
14112                            {
14113                                tag : 'span',
14114                                html : this.fieldLabel
14115                            },
14116                            indicator
14117                        ]
14118
14119                     },
14120                     combobox
14121
14122                 ];
14123
14124             }
14125
14126         } else {
14127             
14128 //                Roo.log(" no label && no align");
14129                 cfg = combobox
14130                      
14131                 
14132         }
14133         
14134         var settings=this;
14135         ['xs','sm','md','lg'].map(function(size){
14136             if (settings[size]) {
14137                 cfg.cls += ' col-' + size + '-' + settings[size];
14138             }
14139         });
14140         
14141         return cfg;
14142         
14143     },
14144     
14145     
14146     
14147     // private
14148     onResize : function(w, h){
14149 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14150 //        if(typeof w == 'number'){
14151 //            var x = w - this.trigger.getWidth();
14152 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14153 //            this.trigger.setStyle('left', x+'px');
14154 //        }
14155     },
14156
14157     // private
14158     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14159
14160     // private
14161     getResizeEl : function(){
14162         return this.inputEl();
14163     },
14164
14165     // private
14166     getPositionEl : function(){
14167         return this.inputEl();
14168     },
14169
14170     // private
14171     alignErrorIcon : function(){
14172         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14173     },
14174
14175     // private
14176     initEvents : function(){
14177         
14178         this.createList();
14179         
14180         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14181         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14182         if(!this.multiple && this.showToggleBtn){
14183             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14184             if(this.hideTrigger){
14185                 this.trigger.setDisplayed(false);
14186             }
14187             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14188         }
14189         
14190         if(this.multiple){
14191             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14192         }
14193         
14194         if(this.removable && !this.editable && !this.tickable){
14195             var close = this.closeTriggerEl();
14196             
14197             if(close){
14198                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14199                 close.on('click', this.removeBtnClick, this, close);
14200             }
14201         }
14202         
14203         //this.trigger.addClassOnOver('x-form-trigger-over');
14204         //this.trigger.addClassOnClick('x-form-trigger-click');
14205         
14206         //if(!this.width){
14207         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14208         //}
14209     },
14210     
14211     closeTriggerEl : function()
14212     {
14213         var close = this.el.select('.roo-combo-removable-btn', true).first();
14214         return close ? close : false;
14215     },
14216     
14217     removeBtnClick : function(e, h, el)
14218     {
14219         e.preventDefault();
14220         
14221         if(this.fireEvent("remove", this) !== false){
14222             this.reset();
14223             this.fireEvent("afterremove", this)
14224         }
14225     },
14226     
14227     createList : function()
14228     {
14229         this.list = Roo.get(document.body).createChild({
14230             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14231             cls: 'typeahead typeahead-long dropdown-menu shadow',
14232             style: 'display:none'
14233         });
14234         
14235         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14236         
14237     },
14238
14239     // private
14240     initTrigger : function(){
14241        
14242     },
14243
14244     // private
14245     onDestroy : function(){
14246         if(this.trigger){
14247             this.trigger.removeAllListeners();
14248           //  this.trigger.remove();
14249         }
14250         //if(this.wrap){
14251         //    this.wrap.remove();
14252         //}
14253         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14254     },
14255
14256     // private
14257     onFocus : function(){
14258         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14259         /*
14260         if(!this.mimicing){
14261             this.wrap.addClass('x-trigger-wrap-focus');
14262             this.mimicing = true;
14263             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14264             if(this.monitorTab){
14265                 this.el.on("keydown", this.checkTab, this);
14266             }
14267         }
14268         */
14269     },
14270
14271     // private
14272     checkTab : function(e){
14273         if(e.getKey() == e.TAB){
14274             this.triggerBlur();
14275         }
14276     },
14277
14278     // private
14279     onBlur : function(){
14280         // do nothing
14281     },
14282
14283     // private
14284     mimicBlur : function(e, t){
14285         /*
14286         if(!this.wrap.contains(t) && this.validateBlur()){
14287             this.triggerBlur();
14288         }
14289         */
14290     },
14291
14292     // private
14293     triggerBlur : function(){
14294         this.mimicing = false;
14295         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14296         if(this.monitorTab){
14297             this.el.un("keydown", this.checkTab, this);
14298         }
14299         //this.wrap.removeClass('x-trigger-wrap-focus');
14300         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14301     },
14302
14303     // private
14304     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14305     validateBlur : function(e, t){
14306         return true;
14307     },
14308
14309     // private
14310     onDisable : function(){
14311         this.inputEl().dom.disabled = true;
14312         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14313         //if(this.wrap){
14314         //    this.wrap.addClass('x-item-disabled');
14315         //}
14316     },
14317
14318     // private
14319     onEnable : function(){
14320         this.inputEl().dom.disabled = false;
14321         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14322         //if(this.wrap){
14323         //    this.el.removeClass('x-item-disabled');
14324         //}
14325     },
14326
14327     // private
14328     onShow : function(){
14329         var ae = this.getActionEl();
14330         
14331         if(ae){
14332             ae.dom.style.display = '';
14333             ae.dom.style.visibility = 'visible';
14334         }
14335     },
14336
14337     // private
14338     
14339     onHide : function(){
14340         var ae = this.getActionEl();
14341         ae.dom.style.display = 'none';
14342     },
14343
14344     /**
14345      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14346      * by an implementing function.
14347      * @method
14348      * @param {EventObject} e
14349      */
14350     onTriggerClick : Roo.emptyFn
14351 });
14352  
14353 /*
14354 * Licence: LGPL
14355 */
14356
14357 /**
14358  * @class Roo.bootstrap.form.CardUploader
14359  * @extends Roo.bootstrap.Button
14360  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14361  * @cfg {Number} errorTimeout default 3000
14362  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14363  * @cfg {Array}  html The button text.
14364
14365  *
14366  * @constructor
14367  * Create a new CardUploader
14368  * @param {Object} config The config object
14369  */
14370
14371 Roo.bootstrap.form.CardUploader = function(config){
14372     
14373  
14374     
14375     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14376     
14377     
14378     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14379         return r.data.id
14380      });
14381     
14382      this.addEvents({
14383          // raw events
14384         /**
14385          * @event preview
14386          * When a image is clicked on - and needs to display a slideshow or similar..
14387          * @param {Roo.bootstrap.Card} this
14388          * @param {Object} The image information data 
14389          *
14390          */
14391         'preview' : true,
14392          /**
14393          * @event download
14394          * When a the download link is clicked
14395          * @param {Roo.bootstrap.Card} this
14396          * @param {Object} The image information data  contains 
14397          */
14398         'download' : true
14399         
14400     });
14401 };
14402  
14403 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14404     
14405      
14406     errorTimeout : 3000,
14407      
14408     images : false,
14409    
14410     fileCollection : false,
14411     allowBlank : true,
14412     
14413     getAutoCreate : function()
14414     {
14415         
14416         var cfg =  {
14417             cls :'form-group' ,
14418             cn : [
14419                
14420                 {
14421                     tag: 'label',
14422                    //cls : 'input-group-addon',
14423                     html : this.fieldLabel
14424
14425                 },
14426
14427                 {
14428                     tag: 'input',
14429                     type : 'hidden',
14430                     name : this.name,
14431                     value : this.value,
14432                     cls : 'd-none  form-control'
14433                 },
14434                 
14435                 {
14436                     tag: 'input',
14437                     multiple : 'multiple',
14438                     type : 'file',
14439                     cls : 'd-none  roo-card-upload-selector'
14440                 },
14441                 
14442                 {
14443                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14444                 },
14445                 {
14446                     cls : 'card-columns roo-card-uploader-container'
14447                 }
14448
14449             ]
14450         };
14451            
14452          
14453         return cfg;
14454     },
14455     
14456     getChildContainer : function() /// what children are added to.
14457     {
14458         return this.containerEl;
14459     },
14460    
14461     getButtonContainer : function() /// what children are added to.
14462     {
14463         return this.el.select(".roo-card-uploader-button-container").first();
14464     },
14465    
14466     initEvents : function()
14467     {
14468         
14469         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14470         
14471         var t = this;
14472         this.addxtype({
14473             xns: Roo.bootstrap,
14474
14475             xtype : 'Button',
14476             container_method : 'getButtonContainer' ,            
14477             html :  this.html, // fix changable?
14478             cls : 'w-100 ',
14479             listeners : {
14480                 'click' : function(btn, e) {
14481                     t.onClick(e);
14482                 }
14483             }
14484         });
14485         
14486         
14487         
14488         
14489         this.urlAPI = (window.createObjectURL && window) || 
14490                                 (window.URL && URL.revokeObjectURL && URL) || 
14491                                 (window.webkitURL && webkitURL);
14492                         
14493          
14494          
14495          
14496         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14497         
14498         this.selectorEl.on('change', this.onFileSelected, this);
14499         if (this.images) {
14500             var t = this;
14501             this.images.forEach(function(img) {
14502                 t.addCard(img)
14503             });
14504             this.images = false;
14505         }
14506         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14507          
14508        
14509     },
14510     
14511    
14512     onClick : function(e)
14513     {
14514         e.preventDefault();
14515          
14516         this.selectorEl.dom.click();
14517          
14518     },
14519     
14520     onFileSelected : function(e)
14521     {
14522         e.preventDefault();
14523         
14524         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14525             return;
14526         }
14527         
14528         Roo.each(this.selectorEl.dom.files, function(file){    
14529             this.addFile(file);
14530         }, this);
14531          
14532     },
14533     
14534       
14535     
14536       
14537     
14538     addFile : function(file)
14539     {
14540            
14541         if(typeof(file) === 'string'){
14542             throw "Add file by name?"; // should not happen
14543             return;
14544         }
14545         
14546         if(!file || !this.urlAPI){
14547             return;
14548         }
14549         
14550         // file;
14551         // file.type;
14552         
14553         var _this = this;
14554         
14555         
14556         var url = _this.urlAPI.createObjectURL( file);
14557            
14558         this.addCard({
14559             id : Roo.bootstrap.form.CardUploader.ID--,
14560             is_uploaded : false,
14561             src : url,
14562             srcfile : file,
14563             title : file.name,
14564             mimetype : file.type,
14565             preview : false,
14566             is_deleted : 0
14567         });
14568         
14569     },
14570     
14571     /**
14572      * addCard - add an Attachment to the uploader
14573      * @param data - the data about the image to upload
14574      *
14575      * {
14576           id : 123
14577           title : "Title of file",
14578           is_uploaded : false,
14579           src : "http://.....",
14580           srcfile : { the File upload object },
14581           mimetype : file.type,
14582           preview : false,
14583           is_deleted : 0
14584           .. any other data...
14585         }
14586      *
14587      * 
14588     */
14589     
14590     addCard : function (data)
14591     {
14592         // hidden input element?
14593         // if the file is not an image...
14594         //then we need to use something other that and header_image
14595         var t = this;
14596         //   remove.....
14597         var footer = [
14598             {
14599                 xns : Roo.bootstrap,
14600                 xtype : 'CardFooter',
14601                  items: [
14602                     {
14603                         xns : Roo.bootstrap,
14604                         xtype : 'Element',
14605                         cls : 'd-flex',
14606                         items : [
14607                             
14608                             {
14609                                 xns : Roo.bootstrap,
14610                                 xtype : 'Button',
14611                                 html : String.format("<small>{0}</small>", data.title),
14612                                 cls : 'col-10 text-left',
14613                                 size: 'sm',
14614                                 weight: 'link',
14615                                 fa : 'download',
14616                                 listeners : {
14617                                     click : function() {
14618                                      
14619                                         t.fireEvent( "download", t, data );
14620                                     }
14621                                 }
14622                             },
14623                           
14624                             {
14625                                 xns : Roo.bootstrap,
14626                                 xtype : 'Button',
14627                                 style: 'max-height: 28px; ',
14628                                 size : 'sm',
14629                                 weight: 'danger',
14630                                 cls : 'col-2',
14631                                 fa : 'times',
14632                                 listeners : {
14633                                     click : function() {
14634                                         t.removeCard(data.id)
14635                                     }
14636                                 }
14637                             }
14638                         ]
14639                     }
14640                     
14641                 ] 
14642             }
14643             
14644         ];
14645         
14646         var cn = this.addxtype(
14647             {
14648                  
14649                 xns : Roo.bootstrap,
14650                 xtype : 'Card',
14651                 closeable : true,
14652                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14653                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14654                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14655                 data : data,
14656                 html : false,
14657                  
14658                 items : footer,
14659                 initEvents : function() {
14660                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14661                     var card = this;
14662                     this.imgEl = this.el.select('.card-img-top').first();
14663                     if (this.imgEl) {
14664                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14665                         this.imgEl.set({ 'pointer' : 'cursor' });
14666                                   
14667                     }
14668                     this.getCardFooter().addClass('p-1');
14669                     
14670                   
14671                 }
14672                 
14673             }
14674         );
14675         // dont' really need ot update items.
14676         // this.items.push(cn);
14677         this.fileCollection.add(cn);
14678         
14679         if (!data.srcfile) {
14680             this.updateInput();
14681             return;
14682         }
14683             
14684         var _t = this;
14685         var reader = new FileReader();
14686         reader.addEventListener("load", function() {  
14687             data.srcdata =  reader.result;
14688             _t.updateInput();
14689         });
14690         reader.readAsDataURL(data.srcfile);
14691         
14692         
14693         
14694     },
14695     removeCard : function(id)
14696     {
14697         
14698         var card  = this.fileCollection.get(id);
14699         card.data.is_deleted = 1;
14700         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14701         //this.fileCollection.remove(card);
14702         //this.items = this.items.filter(function(e) { return e != card });
14703         // dont' really need ot update items.
14704         card.el.dom.parentNode.removeChild(card.el.dom);
14705         this.updateInput();
14706
14707         
14708     },
14709     reset: function()
14710     {
14711         this.fileCollection.each(function(card) {
14712             if (card.el.dom && card.el.dom.parentNode) {
14713                 card.el.dom.parentNode.removeChild(card.el.dom);
14714             }
14715         });
14716         this.fileCollection.clear();
14717         this.updateInput();
14718     },
14719     
14720     updateInput : function()
14721     {
14722          var data = [];
14723         this.fileCollection.each(function(e) {
14724             data.push(e.data);
14725             
14726         });
14727         this.inputEl().dom.value = JSON.stringify(data);
14728         
14729         
14730         
14731     }
14732     
14733     
14734 });
14735
14736
14737 Roo.bootstrap.form.CardUploader.ID = -1;/*
14738  * Based on:
14739  * Ext JS Library 1.1.1
14740  * Copyright(c) 2006-2007, Ext JS, LLC.
14741  *
14742  * Originally Released Under LGPL - original licence link has changed is not relivant.
14743  *
14744  * Fork - LGPL
14745  * <script type="text/javascript">
14746  */
14747
14748
14749 /**
14750  * @class Roo.data.SortTypes
14751  * @static
14752  * Defines the default sorting (casting?) comparison functions used when sorting data.
14753  */
14754 Roo.data.SortTypes = {
14755     /**
14756      * Default sort that does nothing
14757      * @param {Mixed} s The value being converted
14758      * @return {Mixed} The comparison value
14759      */
14760     none : function(s){
14761         return s;
14762     },
14763     
14764     /**
14765      * The regular expression used to strip tags
14766      * @type {RegExp}
14767      * @property
14768      */
14769     stripTagsRE : /<\/?[^>]+>/gi,
14770     
14771     /**
14772      * Strips all HTML tags to sort on text only
14773      * @param {Mixed} s The value being converted
14774      * @return {String} The comparison value
14775      */
14776     asText : function(s){
14777         return String(s).replace(this.stripTagsRE, "");
14778     },
14779     
14780     /**
14781      * Strips all HTML tags to sort on text only - Case insensitive
14782      * @param {Mixed} s The value being converted
14783      * @return {String} The comparison value
14784      */
14785     asUCText : function(s){
14786         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14787     },
14788     
14789     /**
14790      * Case insensitive string
14791      * @param {Mixed} s The value being converted
14792      * @return {String} The comparison value
14793      */
14794     asUCString : function(s) {
14795         return String(s).toUpperCase();
14796     },
14797     
14798     /**
14799      * Date sorting
14800      * @param {Mixed} s The value being converted
14801      * @return {Number} The comparison value
14802      */
14803     asDate : function(s) {
14804         if(!s){
14805             return 0;
14806         }
14807         if(s instanceof Date){
14808             return s.getTime();
14809         }
14810         return Date.parse(String(s));
14811     },
14812     
14813     /**
14814      * Float sorting
14815      * @param {Mixed} s The value being converted
14816      * @return {Float} The comparison value
14817      */
14818     asFloat : function(s) {
14819         var val = parseFloat(String(s).replace(/,/g, ""));
14820         if(isNaN(val)) {
14821             val = 0;
14822         }
14823         return val;
14824     },
14825     
14826     /**
14827      * Integer sorting
14828      * @param {Mixed} s The value being converted
14829      * @return {Number} The comparison value
14830      */
14831     asInt : function(s) {
14832         var val = parseInt(String(s).replace(/,/g, ""));
14833         if(isNaN(val)) {
14834             val = 0;
14835         }
14836         return val;
14837     }
14838 };/*
14839  * Based on:
14840  * Ext JS Library 1.1.1
14841  * Copyright(c) 2006-2007, Ext JS, LLC.
14842  *
14843  * Originally Released Under LGPL - original licence link has changed is not relivant.
14844  *
14845  * Fork - LGPL
14846  * <script type="text/javascript">
14847  */
14848
14849 /**
14850 * @class Roo.data.Record
14851  * Instances of this class encapsulate both record <em>definition</em> information, and record
14852  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14853  * to access Records cached in an {@link Roo.data.Store} object.<br>
14854  * <p>
14855  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14856  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14857  * objects.<br>
14858  * <p>
14859  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14860  * @constructor
14861  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14862  * {@link #create}. The parameters are the same.
14863  * @param {Array} data An associative Array of data values keyed by the field name.
14864  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14865  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14866  * not specified an integer id is generated.
14867  */
14868 Roo.data.Record = function(data, id){
14869     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14870     this.data = data;
14871 };
14872
14873 /**
14874  * Generate a constructor for a specific record layout.
14875  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14876  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14877  * Each field definition object may contain the following properties: <ul>
14878  * <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,
14879  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14880  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14881  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14882  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14883  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14884  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14885  * this may be omitted.</p></li>
14886  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14887  * <ul><li>auto (Default, implies no conversion)</li>
14888  * <li>string</li>
14889  * <li>int</li>
14890  * <li>float</li>
14891  * <li>boolean</li>
14892  * <li>date</li></ul></p></li>
14893  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14894  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14895  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14896  * by the Reader into an object that will be stored in the Record. It is passed the
14897  * following parameters:<ul>
14898  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14899  * </ul></p></li>
14900  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14901  * </ul>
14902  * <br>usage:<br><pre><code>
14903 var TopicRecord = Roo.data.Record.create(
14904     {name: 'title', mapping: 'topic_title'},
14905     {name: 'author', mapping: 'username'},
14906     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14907     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14908     {name: 'lastPoster', mapping: 'user2'},
14909     {name: 'excerpt', mapping: 'post_text'}
14910 );
14911
14912 var myNewRecord = new TopicRecord({
14913     title: 'Do my job please',
14914     author: 'noobie',
14915     totalPosts: 1,
14916     lastPost: new Date(),
14917     lastPoster: 'Animal',
14918     excerpt: 'No way dude!'
14919 });
14920 myStore.add(myNewRecord);
14921 </code></pre>
14922  * @method create
14923  * @static
14924  */
14925 Roo.data.Record.create = function(o){
14926     var f = function(){
14927         f.superclass.constructor.apply(this, arguments);
14928     };
14929     Roo.extend(f, Roo.data.Record);
14930     var p = f.prototype;
14931     p.fields = new Roo.util.MixedCollection(false, function(field){
14932         return field.name;
14933     });
14934     for(var i = 0, len = o.length; i < len; i++){
14935         p.fields.add(new Roo.data.Field(o[i]));
14936     }
14937     f.getField = function(name){
14938         return p.fields.get(name);  
14939     };
14940     return f;
14941 };
14942
14943 Roo.data.Record.AUTO_ID = 1000;
14944 Roo.data.Record.EDIT = 'edit';
14945 Roo.data.Record.REJECT = 'reject';
14946 Roo.data.Record.COMMIT = 'commit';
14947
14948 Roo.data.Record.prototype = {
14949     /**
14950      * Readonly flag - true if this record has been modified.
14951      * @type Boolean
14952      */
14953     dirty : false,
14954     editing : false,
14955     error: null,
14956     modified: null,
14957
14958     // private
14959     join : function(store){
14960         this.store = store;
14961     },
14962
14963     /**
14964      * Set the named field to the specified value.
14965      * @param {String} name The name of the field to set.
14966      * @param {Object} value The value to set the field to.
14967      */
14968     set : function(name, value){
14969         if(this.data[name] == value){
14970             return;
14971         }
14972         this.dirty = true;
14973         if(!this.modified){
14974             this.modified = {};
14975         }
14976         if(typeof this.modified[name] == 'undefined'){
14977             this.modified[name] = this.data[name];
14978         }
14979         this.data[name] = value;
14980         if(!this.editing && this.store){
14981             this.store.afterEdit(this);
14982         }       
14983     },
14984
14985     /**
14986      * Get the value of the named field.
14987      * @param {String} name The name of the field to get the value of.
14988      * @return {Object} The value of the field.
14989      */
14990     get : function(name){
14991         return this.data[name]; 
14992     },
14993
14994     // private
14995     beginEdit : function(){
14996         this.editing = true;
14997         this.modified = {}; 
14998     },
14999
15000     // private
15001     cancelEdit : function(){
15002         this.editing = false;
15003         delete this.modified;
15004     },
15005
15006     // private
15007     endEdit : function(){
15008         this.editing = false;
15009         if(this.dirty && this.store){
15010             this.store.afterEdit(this);
15011         }
15012     },
15013
15014     /**
15015      * Usually called by the {@link Roo.data.Store} which owns the Record.
15016      * Rejects all changes made to the Record since either creation, or the last commit operation.
15017      * Modified fields are reverted to their original values.
15018      * <p>
15019      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15020      * of reject operations.
15021      */
15022     reject : function(){
15023         var m = this.modified;
15024         for(var n in m){
15025             if(typeof m[n] != "function"){
15026                 this.data[n] = m[n];
15027             }
15028         }
15029         this.dirty = false;
15030         delete this.modified;
15031         this.editing = false;
15032         if(this.store){
15033             this.store.afterReject(this);
15034         }
15035     },
15036
15037     /**
15038      * Usually called by the {@link Roo.data.Store} which owns the Record.
15039      * Commits all changes made to the Record since either creation, or the last commit operation.
15040      * <p>
15041      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15042      * of commit operations.
15043      */
15044     commit : function(){
15045         this.dirty = false;
15046         delete this.modified;
15047         this.editing = false;
15048         if(this.store){
15049             this.store.afterCommit(this);
15050         }
15051     },
15052
15053     // private
15054     hasError : function(){
15055         return this.error != null;
15056     },
15057
15058     // private
15059     clearError : function(){
15060         this.error = null;
15061     },
15062
15063     /**
15064      * Creates a copy of this record.
15065      * @param {String} id (optional) A new record id if you don't want to use this record's id
15066      * @return {Record}
15067      */
15068     copy : function(newId) {
15069         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15070     }
15071 };/*
15072  * Based on:
15073  * Ext JS Library 1.1.1
15074  * Copyright(c) 2006-2007, Ext JS, LLC.
15075  *
15076  * Originally Released Under LGPL - original licence link has changed is not relivant.
15077  *
15078  * Fork - LGPL
15079  * <script type="text/javascript">
15080  */
15081
15082
15083
15084 /**
15085  * @class Roo.data.Store
15086  * @extends Roo.util.Observable
15087  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15088  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15089  * <p>
15090  * 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
15091  * has no knowledge of the format of the data returned by the Proxy.<br>
15092  * <p>
15093  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15094  * instances from the data object. These records are cached and made available through accessor functions.
15095  * @constructor
15096  * Creates a new Store.
15097  * @param {Object} config A config object containing the objects needed for the Store to access data,
15098  * and read the data into Records.
15099  */
15100 Roo.data.Store = function(config){
15101     this.data = new Roo.util.MixedCollection(false);
15102     this.data.getKey = function(o){
15103         return o.id;
15104     };
15105     this.baseParams = {};
15106     // private
15107     this.paramNames = {
15108         "start" : "start",
15109         "limit" : "limit",
15110         "sort" : "sort",
15111         "dir" : "dir",
15112         "multisort" : "_multisort"
15113     };
15114
15115     if(config && config.data){
15116         this.inlineData = config.data;
15117         delete config.data;
15118     }
15119
15120     Roo.apply(this, config);
15121     
15122     if(this.reader){ // reader passed
15123         this.reader = Roo.factory(this.reader, Roo.data);
15124         this.reader.xmodule = this.xmodule || false;
15125         if(!this.recordType){
15126             this.recordType = this.reader.recordType;
15127         }
15128         if(this.reader.onMetaChange){
15129             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15130         }
15131     }
15132
15133     if(this.recordType){
15134         this.fields = this.recordType.prototype.fields;
15135     }
15136     this.modified = [];
15137
15138     this.addEvents({
15139         /**
15140          * @event datachanged
15141          * Fires when the data cache has changed, and a widget which is using this Store
15142          * as a Record cache should refresh its view.
15143          * @param {Store} this
15144          */
15145         datachanged : true,
15146         /**
15147          * @event metachange
15148          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15149          * @param {Store} this
15150          * @param {Object} meta The JSON metadata
15151          */
15152         metachange : true,
15153         /**
15154          * @event add
15155          * Fires when Records have been added to the Store
15156          * @param {Store} this
15157          * @param {Roo.data.Record[]} records The array of Records added
15158          * @param {Number} index The index at which the record(s) were added
15159          */
15160         add : true,
15161         /**
15162          * @event remove
15163          * Fires when a Record has been removed from the Store
15164          * @param {Store} this
15165          * @param {Roo.data.Record} record The Record that was removed
15166          * @param {Number} index The index at which the record was removed
15167          */
15168         remove : true,
15169         /**
15170          * @event update
15171          * Fires when a Record has been updated
15172          * @param {Store} this
15173          * @param {Roo.data.Record} record The Record that was updated
15174          * @param {String} operation The update operation being performed.  Value may be one of:
15175          * <pre><code>
15176  Roo.data.Record.EDIT
15177  Roo.data.Record.REJECT
15178  Roo.data.Record.COMMIT
15179          * </code></pre>
15180          */
15181         update : true,
15182         /**
15183          * @event clear
15184          * Fires when the data cache has been cleared.
15185          * @param {Store} this
15186          */
15187         clear : true,
15188         /**
15189          * @event beforeload
15190          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15191          * the load action will be canceled.
15192          * @param {Store} this
15193          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15194          */
15195         beforeload : true,
15196         /**
15197          * @event beforeloadadd
15198          * Fires after a new set of Records has been loaded.
15199          * @param {Store} this
15200          * @param {Roo.data.Record[]} records The Records that were loaded
15201          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15202          */
15203         beforeloadadd : true,
15204         /**
15205          * @event load
15206          * Fires after a new set of Records has been loaded, before they are added to the store.
15207          * @param {Store} this
15208          * @param {Roo.data.Record[]} records The Records that were loaded
15209          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15210          * @params {Object} return from reader
15211          */
15212         load : true,
15213         /**
15214          * @event loadexception
15215          * Fires if an exception occurs in the Proxy during loading.
15216          * Called with the signature of the Proxy's "loadexception" event.
15217          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15218          * 
15219          * @param {Proxy} 
15220          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15221          * @param {Object} load options 
15222          * @param {Object} jsonData from your request (normally this contains the Exception)
15223          */
15224         loadexception : true
15225     });
15226     
15227     if(this.proxy){
15228         this.proxy = Roo.factory(this.proxy, Roo.data);
15229         this.proxy.xmodule = this.xmodule || false;
15230         this.relayEvents(this.proxy,  ["loadexception"]);
15231     }
15232     this.sortToggle = {};
15233     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15234
15235     Roo.data.Store.superclass.constructor.call(this);
15236
15237     if(this.inlineData){
15238         this.loadData(this.inlineData);
15239         delete this.inlineData;
15240     }
15241 };
15242
15243 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15244      /**
15245     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15246     * without a remote query - used by combo/forms at present.
15247     */
15248     
15249     /**
15250     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15251     */
15252     /**
15253     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15254     */
15255     /**
15256     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15257     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15258     */
15259     /**
15260     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15261     * on any HTTP request
15262     */
15263     /**
15264     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15265     */
15266     /**
15267     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15268     */
15269     multiSort: false,
15270     /**
15271     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15272     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15273     */
15274     remoteSort : false,
15275
15276     /**
15277     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15278      * loaded or when a record is removed. (defaults to false).
15279     */
15280     pruneModifiedRecords : false,
15281
15282     // private
15283     lastOptions : null,
15284
15285     /**
15286      * Add Records to the Store and fires the add event.
15287      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15288      */
15289     add : function(records){
15290         records = [].concat(records);
15291         for(var i = 0, len = records.length; i < len; i++){
15292             records[i].join(this);
15293         }
15294         var index = this.data.length;
15295         this.data.addAll(records);
15296         this.fireEvent("add", this, records, index);
15297     },
15298
15299     /**
15300      * Remove a Record from the Store and fires the remove event.
15301      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15302      */
15303     remove : function(record){
15304         var index = this.data.indexOf(record);
15305         this.data.removeAt(index);
15306  
15307         if(this.pruneModifiedRecords){
15308             this.modified.remove(record);
15309         }
15310         this.fireEvent("remove", this, record, index);
15311     },
15312
15313     /**
15314      * Remove all Records from the Store and fires the clear event.
15315      */
15316     removeAll : function(){
15317         this.data.clear();
15318         if(this.pruneModifiedRecords){
15319             this.modified = [];
15320         }
15321         this.fireEvent("clear", this);
15322     },
15323
15324     /**
15325      * Inserts Records to the Store at the given index and fires the add event.
15326      * @param {Number} index The start index at which to insert the passed Records.
15327      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15328      */
15329     insert : function(index, records){
15330         records = [].concat(records);
15331         for(var i = 0, len = records.length; i < len; i++){
15332             this.data.insert(index, records[i]);
15333             records[i].join(this);
15334         }
15335         this.fireEvent("add", this, records, index);
15336     },
15337
15338     /**
15339      * Get the index within the cache of the passed Record.
15340      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15341      * @return {Number} The index of the passed Record. Returns -1 if not found.
15342      */
15343     indexOf : function(record){
15344         return this.data.indexOf(record);
15345     },
15346
15347     /**
15348      * Get the index within the cache of the Record with the passed id.
15349      * @param {String} id The id of the Record to find.
15350      * @return {Number} The index of the Record. Returns -1 if not found.
15351      */
15352     indexOfId : function(id){
15353         return this.data.indexOfKey(id);
15354     },
15355
15356     /**
15357      * Get the Record with the specified id.
15358      * @param {String} id The id of the Record to find.
15359      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15360      */
15361     getById : function(id){
15362         return this.data.key(id);
15363     },
15364
15365     /**
15366      * Get the Record at the specified index.
15367      * @param {Number} index The index of the Record to find.
15368      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15369      */
15370     getAt : function(index){
15371         return this.data.itemAt(index);
15372     },
15373
15374     /**
15375      * Returns a range of Records between specified indices.
15376      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15377      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15378      * @return {Roo.data.Record[]} An array of Records
15379      */
15380     getRange : function(start, end){
15381         return this.data.getRange(start, end);
15382     },
15383
15384     // private
15385     storeOptions : function(o){
15386         o = Roo.apply({}, o);
15387         delete o.callback;
15388         delete o.scope;
15389         this.lastOptions = o;
15390     },
15391
15392     /**
15393      * Loads the Record cache from the configured Proxy using the configured Reader.
15394      * <p>
15395      * If using remote paging, then the first load call must specify the <em>start</em>
15396      * and <em>limit</em> properties in the options.params property to establish the initial
15397      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15398      * <p>
15399      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15400      * and this call will return before the new data has been loaded. Perform any post-processing
15401      * in a callback function, or in a "load" event handler.</strong>
15402      * <p>
15403      * @param {Object} options An object containing properties which control loading options:<ul>
15404      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15405      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15406      * <pre>
15407                 {
15408                     data : data,  // array of key=>value data like JsonReader
15409                     total : data.length,
15410                     success : true
15411                     
15412                 }
15413         </pre>
15414             }.</li>
15415      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15416      * passed the following arguments:<ul>
15417      * <li>r : Roo.data.Record[]</li>
15418      * <li>options: Options object from the load call</li>
15419      * <li>success: Boolean success indicator</li></ul></li>
15420      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15421      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15422      * </ul>
15423      */
15424     load : function(options){
15425         options = options || {};
15426         if(this.fireEvent("beforeload", this, options) !== false){
15427             this.storeOptions(options);
15428             var p = Roo.apply(options.params || {}, this.baseParams);
15429             // if meta was not loaded from remote source.. try requesting it.
15430             if (!this.reader.metaFromRemote) {
15431                 p._requestMeta = 1;
15432             }
15433             if(this.sortInfo && this.remoteSort){
15434                 var pn = this.paramNames;
15435                 p[pn["sort"]] = this.sortInfo.field;
15436                 p[pn["dir"]] = this.sortInfo.direction;
15437             }
15438             if (this.multiSort) {
15439                 var pn = this.paramNames;
15440                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15441             }
15442             
15443             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15444         }
15445     },
15446
15447     /**
15448      * Reloads the Record cache from the configured Proxy using the configured Reader and
15449      * the options from the last load operation performed.
15450      * @param {Object} options (optional) An object containing properties which may override the options
15451      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15452      * the most recently used options are reused).
15453      */
15454     reload : function(options){
15455         this.load(Roo.applyIf(options||{}, this.lastOptions));
15456     },
15457
15458     // private
15459     // Called as a callback by the Reader during a load operation.
15460     loadRecords : function(o, options, success){
15461          
15462         if(!o){
15463             if(success !== false){
15464                 this.fireEvent("load", this, [], options, o);
15465             }
15466             if(options.callback){
15467                 options.callback.call(options.scope || this, [], options, false);
15468             }
15469             return;
15470         }
15471         // if data returned failure - throw an exception.
15472         if (o.success === false) {
15473             // show a message if no listener is registered.
15474             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15475                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15476             }
15477             // loadmask wil be hooked into this..
15478             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15479             return;
15480         }
15481         var r = o.records, t = o.totalRecords || r.length;
15482         
15483         this.fireEvent("beforeloadadd", this, r, options, o);
15484         
15485         if(!options || options.add !== true){
15486             if(this.pruneModifiedRecords){
15487                 this.modified = [];
15488             }
15489             for(var i = 0, len = r.length; i < len; i++){
15490                 r[i].join(this);
15491             }
15492             if(this.snapshot){
15493                 this.data = this.snapshot;
15494                 delete this.snapshot;
15495             }
15496             this.data.clear();
15497             this.data.addAll(r);
15498             this.totalLength = t;
15499             this.applySort();
15500             this.fireEvent("datachanged", this);
15501         }else{
15502             this.totalLength = Math.max(t, this.data.length+r.length);
15503             this.add(r);
15504         }
15505         
15506         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15507                 
15508             var e = new Roo.data.Record({});
15509
15510             e.set(this.parent.displayField, this.parent.emptyTitle);
15511             e.set(this.parent.valueField, '');
15512
15513             this.insert(0, e);
15514         }
15515             
15516         this.fireEvent("load", this, r, options, o);
15517         if(options.callback){
15518             options.callback.call(options.scope || this, r, options, true);
15519         }
15520     },
15521
15522
15523     /**
15524      * Loads data from a passed data block. A Reader which understands the format of the data
15525      * must have been configured in the constructor.
15526      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15527      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15528      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15529      */
15530     loadData : function(o, append){
15531         var r = this.reader.readRecords(o);
15532         this.loadRecords(r, {add: append}, true);
15533     },
15534     
15535      /**
15536      * using 'cn' the nested child reader read the child array into it's child stores.
15537      * @param {Object} rec The record with a 'children array
15538      */
15539     loadDataFromChildren : function(rec)
15540     {
15541         this.loadData(this.reader.toLoadData(rec));
15542     },
15543     
15544
15545     /**
15546      * Gets the number of cached records.
15547      * <p>
15548      * <em>If using paging, this may not be the total size of the dataset. If the data object
15549      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15550      * the data set size</em>
15551      */
15552     getCount : function(){
15553         return this.data.length || 0;
15554     },
15555
15556     /**
15557      * Gets the total number of records in the dataset as returned by the server.
15558      * <p>
15559      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15560      * the dataset size</em>
15561      */
15562     getTotalCount : function(){
15563         return this.totalLength || 0;
15564     },
15565
15566     /**
15567      * Returns the sort state of the Store as an object with two properties:
15568      * <pre><code>
15569  field {String} The name of the field by which the Records are sorted
15570  direction {String} The sort order, "ASC" or "DESC"
15571      * </code></pre>
15572      */
15573     getSortState : function(){
15574         return this.sortInfo;
15575     },
15576
15577     // private
15578     applySort : function(){
15579         if(this.sortInfo && !this.remoteSort){
15580             var s = this.sortInfo, f = s.field;
15581             var st = this.fields.get(f).sortType;
15582             var fn = function(r1, r2){
15583                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15584                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15585             };
15586             this.data.sort(s.direction, fn);
15587             if(this.snapshot && this.snapshot != this.data){
15588                 this.snapshot.sort(s.direction, fn);
15589             }
15590         }
15591     },
15592
15593     /**
15594      * Sets the default sort column and order to be used by the next load operation.
15595      * @param {String} fieldName The name of the field to sort by.
15596      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15597      */
15598     setDefaultSort : function(field, dir){
15599         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15600     },
15601
15602     /**
15603      * Sort the Records.
15604      * If remote sorting is used, the sort is performed on the server, and the cache is
15605      * reloaded. If local sorting is used, the cache is sorted internally.
15606      * @param {String} fieldName The name of the field to sort by.
15607      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15608      */
15609     sort : function(fieldName, dir){
15610         var f = this.fields.get(fieldName);
15611         if(!dir){
15612             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15613             
15614             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15615                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15616             }else{
15617                 dir = f.sortDir;
15618             }
15619         }
15620         this.sortToggle[f.name] = dir;
15621         this.sortInfo = {field: f.name, direction: dir};
15622         if(!this.remoteSort){
15623             this.applySort();
15624             this.fireEvent("datachanged", this);
15625         }else{
15626             this.load(this.lastOptions);
15627         }
15628     },
15629
15630     /**
15631      * Calls the specified function for each of the Records in the cache.
15632      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15633      * Returning <em>false</em> aborts and exits the iteration.
15634      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15635      */
15636     each : function(fn, scope){
15637         this.data.each(fn, scope);
15638     },
15639
15640     /**
15641      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15642      * (e.g., during paging).
15643      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15644      */
15645     getModifiedRecords : function(){
15646         return this.modified;
15647     },
15648
15649     // private
15650     createFilterFn : function(property, value, anyMatch){
15651         if(!value.exec){ // not a regex
15652             value = String(value);
15653             if(value.length == 0){
15654                 return false;
15655             }
15656             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15657         }
15658         return function(r){
15659             return value.test(r.data[property]);
15660         };
15661     },
15662
15663     /**
15664      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15665      * @param {String} property A field on your records
15666      * @param {Number} start The record index to start at (defaults to 0)
15667      * @param {Number} end The last record index to include (defaults to length - 1)
15668      * @return {Number} The sum
15669      */
15670     sum : function(property, start, end){
15671         var rs = this.data.items, v = 0;
15672         start = start || 0;
15673         end = (end || end === 0) ? end : rs.length-1;
15674
15675         for(var i = start; i <= end; i++){
15676             v += (rs[i].data[property] || 0);
15677         }
15678         return v;
15679     },
15680
15681     /**
15682      * Filter the records by a specified property.
15683      * @param {String} field A field on your records
15684      * @param {String/RegExp} value Either a string that the field
15685      * should start with or a RegExp to test against the field
15686      * @param {Boolean} anyMatch True to match any part not just the beginning
15687      */
15688     filter : function(property, value, anyMatch){
15689         var fn = this.createFilterFn(property, value, anyMatch);
15690         return fn ? this.filterBy(fn) : this.clearFilter();
15691     },
15692
15693     /**
15694      * Filter by a function. The specified function will be called with each
15695      * record in this data source. If the function returns true the record is included,
15696      * otherwise it is filtered.
15697      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15698      * @param {Object} scope (optional) The scope of the function (defaults to this)
15699      */
15700     filterBy : function(fn, scope){
15701         this.snapshot = this.snapshot || this.data;
15702         this.data = this.queryBy(fn, scope||this);
15703         this.fireEvent("datachanged", this);
15704     },
15705
15706     /**
15707      * Query the records by a specified property.
15708      * @param {String} field A field on your records
15709      * @param {String/RegExp} value Either a string that the field
15710      * should start with or a RegExp to test against the field
15711      * @param {Boolean} anyMatch True to match any part not just the beginning
15712      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15713      */
15714     query : function(property, value, anyMatch){
15715         var fn = this.createFilterFn(property, value, anyMatch);
15716         return fn ? this.queryBy(fn) : this.data.clone();
15717     },
15718
15719     /**
15720      * Query by a function. The specified function will be called with each
15721      * record in this data source. If the function returns true the record is included
15722      * in the results.
15723      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15724      * @param {Object} scope (optional) The scope of the function (defaults to this)
15725       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15726      **/
15727     queryBy : function(fn, scope){
15728         var data = this.snapshot || this.data;
15729         return data.filterBy(fn, scope||this);
15730     },
15731
15732     /**
15733      * Collects unique values for a particular dataIndex from this store.
15734      * @param {String} dataIndex The property to collect
15735      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15736      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15737      * @return {Array} An array of the unique values
15738      **/
15739     collect : function(dataIndex, allowNull, bypassFilter){
15740         var d = (bypassFilter === true && this.snapshot) ?
15741                 this.snapshot.items : this.data.items;
15742         var v, sv, r = [], l = {};
15743         for(var i = 0, len = d.length; i < len; i++){
15744             v = d[i].data[dataIndex];
15745             sv = String(v);
15746             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15747                 l[sv] = true;
15748                 r[r.length] = v;
15749             }
15750         }
15751         return r;
15752     },
15753
15754     /**
15755      * Revert to a view of the Record cache with no filtering applied.
15756      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15757      */
15758     clearFilter : function(suppressEvent){
15759         if(this.snapshot && this.snapshot != this.data){
15760             this.data = this.snapshot;
15761             delete this.snapshot;
15762             if(suppressEvent !== true){
15763                 this.fireEvent("datachanged", this);
15764             }
15765         }
15766     },
15767
15768     // private
15769     afterEdit : function(record){
15770         if(this.modified.indexOf(record) == -1){
15771             this.modified.push(record);
15772         }
15773         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15774     },
15775     
15776     // private
15777     afterReject : function(record){
15778         this.modified.remove(record);
15779         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15780     },
15781
15782     // private
15783     afterCommit : function(record){
15784         this.modified.remove(record);
15785         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15786     },
15787
15788     /**
15789      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15790      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15791      */
15792     commitChanges : function(){
15793         var m = this.modified.slice(0);
15794         this.modified = [];
15795         for(var i = 0, len = m.length; i < len; i++){
15796             m[i].commit();
15797         }
15798     },
15799
15800     /**
15801      * Cancel outstanding changes on all changed records.
15802      */
15803     rejectChanges : function(){
15804         var m = this.modified.slice(0);
15805         this.modified = [];
15806         for(var i = 0, len = m.length; i < len; i++){
15807             m[i].reject();
15808         }
15809     },
15810
15811     onMetaChange : function(meta, rtype, o){
15812         this.recordType = rtype;
15813         this.fields = rtype.prototype.fields;
15814         delete this.snapshot;
15815         this.sortInfo = meta.sortInfo || this.sortInfo;
15816         this.modified = [];
15817         this.fireEvent('metachange', this, this.reader.meta);
15818     },
15819     
15820     moveIndex : function(data, type)
15821     {
15822         var index = this.indexOf(data);
15823         
15824         var newIndex = index + type;
15825         
15826         this.remove(data);
15827         
15828         this.insert(newIndex, data);
15829         
15830     }
15831 });/*
15832  * Based on:
15833  * Ext JS Library 1.1.1
15834  * Copyright(c) 2006-2007, Ext JS, LLC.
15835  *
15836  * Originally Released Under LGPL - original licence link has changed is not relivant.
15837  *
15838  * Fork - LGPL
15839  * <script type="text/javascript">
15840  */
15841
15842 /**
15843  * @class Roo.data.SimpleStore
15844  * @extends Roo.data.Store
15845  * Small helper class to make creating Stores from Array data easier.
15846  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15847  * @cfg {Array} fields An array of field definition objects, or field name strings.
15848  * @cfg {Object} an existing reader (eg. copied from another store)
15849  * @cfg {Array} data The multi-dimensional array of data
15850  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15851  * @cfg {Roo.data.Reader} reader  [not-required] 
15852  * @constructor
15853  * @param {Object} config
15854  */
15855 Roo.data.SimpleStore = function(config)
15856 {
15857     Roo.data.SimpleStore.superclass.constructor.call(this, {
15858         isLocal : true,
15859         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15860                 id: config.id
15861             },
15862             Roo.data.Record.create(config.fields)
15863         ),
15864         proxy : new Roo.data.MemoryProxy(config.data)
15865     });
15866     this.load();
15867 };
15868 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15869  * Based on:
15870  * Ext JS Library 1.1.1
15871  * Copyright(c) 2006-2007, Ext JS, LLC.
15872  *
15873  * Originally Released Under LGPL - original licence link has changed is not relivant.
15874  *
15875  * Fork - LGPL
15876  * <script type="text/javascript">
15877  */
15878
15879 /**
15880 /**
15881  * @extends Roo.data.Store
15882  * @class Roo.data.JsonStore
15883  * Small helper class to make creating Stores for JSON data easier. <br/>
15884 <pre><code>
15885 var store = new Roo.data.JsonStore({
15886     url: 'get-images.php',
15887     root: 'images',
15888     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15889 });
15890 </code></pre>
15891  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15892  * JsonReader and HttpProxy (unless inline data is provided).</b>
15893  * @cfg {Array} fields An array of field definition objects, or field name strings.
15894  * @constructor
15895  * @param {Object} config
15896  */
15897 Roo.data.JsonStore = function(c){
15898     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15899         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15900         reader: new Roo.data.JsonReader(c, c.fields)
15901     }));
15902 };
15903 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15904  * Based on:
15905  * Ext JS Library 1.1.1
15906  * Copyright(c) 2006-2007, Ext JS, LLC.
15907  *
15908  * Originally Released Under LGPL - original licence link has changed is not relivant.
15909  *
15910  * Fork - LGPL
15911  * <script type="text/javascript">
15912  */
15913
15914  
15915 Roo.data.Field = function(config){
15916     if(typeof config == "string"){
15917         config = {name: config};
15918     }
15919     Roo.apply(this, config);
15920     
15921     if(!this.type){
15922         this.type = "auto";
15923     }
15924     
15925     var st = Roo.data.SortTypes;
15926     // named sortTypes are supported, here we look them up
15927     if(typeof this.sortType == "string"){
15928         this.sortType = st[this.sortType];
15929     }
15930     
15931     // set default sortType for strings and dates
15932     if(!this.sortType){
15933         switch(this.type){
15934             case "string":
15935                 this.sortType = st.asUCString;
15936                 break;
15937             case "date":
15938                 this.sortType = st.asDate;
15939                 break;
15940             default:
15941                 this.sortType = st.none;
15942         }
15943     }
15944
15945     // define once
15946     var stripRe = /[\$,%]/g;
15947
15948     // prebuilt conversion function for this field, instead of
15949     // switching every time we're reading a value
15950     if(!this.convert){
15951         var cv, dateFormat = this.dateFormat;
15952         switch(this.type){
15953             case "":
15954             case "auto":
15955             case undefined:
15956                 cv = function(v){ return v; };
15957                 break;
15958             case "string":
15959                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15960                 break;
15961             case "int":
15962                 cv = function(v){
15963                     return v !== undefined && v !== null && v !== '' ?
15964                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15965                     };
15966                 break;
15967             case "float":
15968                 cv = function(v){
15969                     return v !== undefined && v !== null && v !== '' ?
15970                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15971                     };
15972                 break;
15973             case "bool":
15974             case "boolean":
15975                 cv = function(v){ return v === true || v === "true" || v == 1; };
15976                 break;
15977             case "date":
15978                 cv = function(v){
15979                     if(!v){
15980                         return '';
15981                     }
15982                     if(v instanceof Date){
15983                         return v;
15984                     }
15985                     if(dateFormat){
15986                         if(dateFormat == "timestamp"){
15987                             return new Date(v*1000);
15988                         }
15989                         return Date.parseDate(v, dateFormat);
15990                     }
15991                     var parsed = Date.parse(v);
15992                     return parsed ? new Date(parsed) : null;
15993                 };
15994              break;
15995             
15996         }
15997         this.convert = cv;
15998     }
15999 };
16000
16001 Roo.data.Field.prototype = {
16002     dateFormat: null,
16003     defaultValue: "",
16004     mapping: null,
16005     sortType : null,
16006     sortDir : "ASC"
16007 };/*
16008  * Based on:
16009  * Ext JS Library 1.1.1
16010  * Copyright(c) 2006-2007, Ext JS, LLC.
16011  *
16012  * Originally Released Under LGPL - original licence link has changed is not relivant.
16013  *
16014  * Fork - LGPL
16015  * <script type="text/javascript">
16016  */
16017  
16018 // Base class for reading structured data from a data source.  This class is intended to be
16019 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
16020
16021 /**
16022  * @class Roo.data.DataReader
16023  * @abstract
16024  * Base class for reading structured data from a data source.  This class is intended to be
16025  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
16026  */
16027
16028 Roo.data.DataReader = function(meta, recordType){
16029     
16030     this.meta = meta;
16031     
16032     this.recordType = recordType instanceof Array ? 
16033         Roo.data.Record.create(recordType) : recordType;
16034 };
16035
16036 Roo.data.DataReader.prototype = {
16037     
16038     
16039     readerType : 'Data',
16040      /**
16041      * Create an empty record
16042      * @param {Object} data (optional) - overlay some values
16043      * @return {Roo.data.Record} record created.
16044      */
16045     newRow :  function(d) {
16046         var da =  {};
16047         this.recordType.prototype.fields.each(function(c) {
16048             switch( c.type) {
16049                 case 'int' : da[c.name] = 0; break;
16050                 case 'date' : da[c.name] = new Date(); break;
16051                 case 'float' : da[c.name] = 0.0; break;
16052                 case 'boolean' : da[c.name] = false; break;
16053                 default : da[c.name] = ""; break;
16054             }
16055             
16056         });
16057         return new this.recordType(Roo.apply(da, d));
16058     }
16059     
16060     
16061 };/*
16062  * Based on:
16063  * Ext JS Library 1.1.1
16064  * Copyright(c) 2006-2007, Ext JS, LLC.
16065  *
16066  * Originally Released Under LGPL - original licence link has changed is not relivant.
16067  *
16068  * Fork - LGPL
16069  * <script type="text/javascript">
16070  */
16071
16072 /**
16073  * @class Roo.data.DataProxy
16074  * @extends Roo.util.Observable
16075  * @abstract
16076  * This class is an abstract base class for implementations which provide retrieval of
16077  * unformatted data objects.<br>
16078  * <p>
16079  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16080  * (of the appropriate type which knows how to parse the data object) to provide a block of
16081  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16082  * <p>
16083  * Custom implementations must implement the load method as described in
16084  * {@link Roo.data.HttpProxy#load}.
16085  */
16086 Roo.data.DataProxy = function(){
16087     this.addEvents({
16088         /**
16089          * @event beforeload
16090          * Fires before a network request is made to retrieve a data object.
16091          * @param {Object} This DataProxy object.
16092          * @param {Object} params The params parameter to the load function.
16093          */
16094         beforeload : true,
16095         /**
16096          * @event load
16097          * Fires before the load method's callback is called.
16098          * @param {Object} This DataProxy object.
16099          * @param {Object} o The data object.
16100          * @param {Object} arg The callback argument object passed to the load function.
16101          */
16102         load : true,
16103         /**
16104          * @event loadexception
16105          * Fires if an Exception occurs during data retrieval.
16106          * @param {Object} This DataProxy object.
16107          * @param {Object} o The data object.
16108          * @param {Object} arg The callback argument object passed to the load function.
16109          * @param {Object} e The Exception.
16110          */
16111         loadexception : true
16112     });
16113     Roo.data.DataProxy.superclass.constructor.call(this);
16114 };
16115
16116 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16117
16118     /**
16119      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16120      */
16121 /*
16122  * Based on:
16123  * Ext JS Library 1.1.1
16124  * Copyright(c) 2006-2007, Ext JS, LLC.
16125  *
16126  * Originally Released Under LGPL - original licence link has changed is not relivant.
16127  *
16128  * Fork - LGPL
16129  * <script type="text/javascript">
16130  */
16131 /**
16132  * @class Roo.data.MemoryProxy
16133  * @extends Roo.data.DataProxy
16134  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16135  * to the Reader when its load method is called.
16136  * @constructor
16137  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16138  */
16139 Roo.data.MemoryProxy = function(config){
16140     var data = config;
16141     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16142         data = config.data;
16143     }
16144     Roo.data.MemoryProxy.superclass.constructor.call(this);
16145     this.data = data;
16146 };
16147
16148 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16149     
16150     /**
16151      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16152      */
16153     /**
16154      * Load data from the requested source (in this case an in-memory
16155      * data object passed to the constructor), read the data object into
16156      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16157      * process that block using the passed callback.
16158      * @param {Object} params This parameter is not used by the MemoryProxy class.
16159      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16160      * object into a block of Roo.data.Records.
16161      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16162      * The function must be passed <ul>
16163      * <li>The Record block object</li>
16164      * <li>The "arg" argument from the load function</li>
16165      * <li>A boolean success indicator</li>
16166      * </ul>
16167      * @param {Object} scope The scope in which to call the callback
16168      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16169      */
16170     load : function(params, reader, callback, scope, arg){
16171         params = params || {};
16172         var result;
16173         try {
16174             result = reader.readRecords(params.data ? params.data :this.data);
16175         }catch(e){
16176             this.fireEvent("loadexception", this, arg, null, e);
16177             callback.call(scope, null, arg, false);
16178             return;
16179         }
16180         callback.call(scope, result, arg, true);
16181     },
16182     
16183     // private
16184     update : function(params, records){
16185         
16186     }
16187 });/*
16188  * Based on:
16189  * Ext JS Library 1.1.1
16190  * Copyright(c) 2006-2007, Ext JS, LLC.
16191  *
16192  * Originally Released Under LGPL - original licence link has changed is not relivant.
16193  *
16194  * Fork - LGPL
16195  * <script type="text/javascript">
16196  */
16197 /**
16198  * @class Roo.data.HttpProxy
16199  * @extends Roo.data.DataProxy
16200  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16201  * configured to reference a certain URL.<br><br>
16202  * <p>
16203  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16204  * from which the running page was served.<br><br>
16205  * <p>
16206  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16207  * <p>
16208  * Be aware that to enable the browser to parse an XML document, the server must set
16209  * the Content-Type header in the HTTP response to "text/xml".
16210  * @constructor
16211  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16212  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16213  * will be used to make the request.
16214  */
16215 Roo.data.HttpProxy = function(conn){
16216     Roo.data.HttpProxy.superclass.constructor.call(this);
16217     // is conn a conn config or a real conn?
16218     this.conn = conn;
16219     this.useAjax = !conn || !conn.events;
16220   
16221 };
16222
16223 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16224     // thse are take from connection...
16225     
16226     /**
16227      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16228      */
16229     /**
16230      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16231      * extra parameters to each request made by this object. (defaults to undefined)
16232      */
16233     /**
16234      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16235      *  to each request made by this object. (defaults to undefined)
16236      */
16237     /**
16238      * @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)
16239      */
16240     /**
16241      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16242      */
16243      /**
16244      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16245      * @type Boolean
16246      */
16247   
16248
16249     /**
16250      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16251      * @type Boolean
16252      */
16253     /**
16254      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16255      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16256      * a finer-grained basis than the DataProxy events.
16257      */
16258     getConnection : function(){
16259         return this.useAjax ? Roo.Ajax : this.conn;
16260     },
16261
16262     /**
16263      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16264      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16265      * process that block using the passed callback.
16266      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16267      * for the request to the remote server.
16268      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16269      * object into a block of Roo.data.Records.
16270      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16271      * The function must be passed <ul>
16272      * <li>The Record block object</li>
16273      * <li>The "arg" argument from the load function</li>
16274      * <li>A boolean success indicator</li>
16275      * </ul>
16276      * @param {Object} scope The scope in which to call the callback
16277      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16278      */
16279     load : function(params, reader, callback, scope, arg){
16280         if(this.fireEvent("beforeload", this, params) !== false){
16281             var  o = {
16282                 params : params || {},
16283                 request: {
16284                     callback : callback,
16285                     scope : scope,
16286                     arg : arg
16287                 },
16288                 reader: reader,
16289                 callback : this.loadResponse,
16290                 scope: this
16291             };
16292             if(this.useAjax){
16293                 Roo.applyIf(o, this.conn);
16294                 if(this.activeRequest){
16295                     Roo.Ajax.abort(this.activeRequest);
16296                 }
16297                 this.activeRequest = Roo.Ajax.request(o);
16298             }else{
16299                 this.conn.request(o);
16300             }
16301         }else{
16302             callback.call(scope||this, null, arg, false);
16303         }
16304     },
16305
16306     // private
16307     loadResponse : function(o, success, response){
16308         delete this.activeRequest;
16309         if(!success){
16310             this.fireEvent("loadexception", this, o, response);
16311             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16312             return;
16313         }
16314         var result;
16315         try {
16316             result = o.reader.read(response);
16317         }catch(e){
16318             o.success = false;
16319             o.raw = { errorMsg : response.responseText };
16320             this.fireEvent("loadexception", this, o, response, e);
16321             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16322             return;
16323         }
16324         
16325         this.fireEvent("load", this, o, o.request.arg);
16326         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16327     },
16328
16329     // private
16330     update : function(dataSet){
16331
16332     },
16333
16334     // private
16335     updateResponse : function(dataSet){
16336
16337     }
16338 });/*
16339  * Based on:
16340  * Ext JS Library 1.1.1
16341  * Copyright(c) 2006-2007, Ext JS, LLC.
16342  *
16343  * Originally Released Under LGPL - original licence link has changed is not relivant.
16344  *
16345  * Fork - LGPL
16346  * <script type="text/javascript">
16347  */
16348
16349 /**
16350  * @class Roo.data.ScriptTagProxy
16351  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16352  * other than the originating domain of the running page.<br><br>
16353  * <p>
16354  * <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
16355  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16356  * <p>
16357  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16358  * source code that is used as the source inside a &lt;script> tag.<br><br>
16359  * <p>
16360  * In order for the browser to process the returned data, the server must wrap the data object
16361  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16362  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16363  * depending on whether the callback name was passed:
16364  * <p>
16365  * <pre><code>
16366 boolean scriptTag = false;
16367 String cb = request.getParameter("callback");
16368 if (cb != null) {
16369     scriptTag = true;
16370     response.setContentType("text/javascript");
16371 } else {
16372     response.setContentType("application/x-json");
16373 }
16374 Writer out = response.getWriter();
16375 if (scriptTag) {
16376     out.write(cb + "(");
16377 }
16378 out.print(dataBlock.toJsonString());
16379 if (scriptTag) {
16380     out.write(");");
16381 }
16382 </pre></code>
16383  *
16384  * @constructor
16385  * @param {Object} config A configuration object.
16386  */
16387 Roo.data.ScriptTagProxy = function(config){
16388     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16389     Roo.apply(this, config);
16390     this.head = document.getElementsByTagName("head")[0];
16391 };
16392
16393 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16394
16395 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16396     /**
16397      * @cfg {String} url The URL from which to request the data object.
16398      */
16399     /**
16400      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16401      */
16402     timeout : 30000,
16403     /**
16404      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16405      * the server the name of the callback function set up by the load call to process the returned data object.
16406      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16407      * javascript output which calls this named function passing the data object as its only parameter.
16408      */
16409     callbackParam : "callback",
16410     /**
16411      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16412      * name to the request.
16413      */
16414     nocache : true,
16415
16416     /**
16417      * Load data from the configured URL, read the data object into
16418      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16419      * process that block using the passed callback.
16420      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16421      * for the request to the remote server.
16422      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16423      * object into a block of Roo.data.Records.
16424      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16425      * The function must be passed <ul>
16426      * <li>The Record block object</li>
16427      * <li>The "arg" argument from the load function</li>
16428      * <li>A boolean success indicator</li>
16429      * </ul>
16430      * @param {Object} scope The scope in which to call the callback
16431      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16432      */
16433     load : function(params, reader, callback, scope, arg){
16434         if(this.fireEvent("beforeload", this, params) !== false){
16435
16436             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16437
16438             var url = this.url;
16439             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16440             if(this.nocache){
16441                 url += "&_dc=" + (new Date().getTime());
16442             }
16443             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16444             var trans = {
16445                 id : transId,
16446                 cb : "stcCallback"+transId,
16447                 scriptId : "stcScript"+transId,
16448                 params : params,
16449                 arg : arg,
16450                 url : url,
16451                 callback : callback,
16452                 scope : scope,
16453                 reader : reader
16454             };
16455             var conn = this;
16456
16457             window[trans.cb] = function(o){
16458                 conn.handleResponse(o, trans);
16459             };
16460
16461             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16462
16463             if(this.autoAbort !== false){
16464                 this.abort();
16465             }
16466
16467             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16468
16469             var script = document.createElement("script");
16470             script.setAttribute("src", url);
16471             script.setAttribute("type", "text/javascript");
16472             script.setAttribute("id", trans.scriptId);
16473             this.head.appendChild(script);
16474
16475             this.trans = trans;
16476         }else{
16477             callback.call(scope||this, null, arg, false);
16478         }
16479     },
16480
16481     // private
16482     isLoading : function(){
16483         return this.trans ? true : false;
16484     },
16485
16486     /**
16487      * Abort the current server request.
16488      */
16489     abort : function(){
16490         if(this.isLoading()){
16491             this.destroyTrans(this.trans);
16492         }
16493     },
16494
16495     // private
16496     destroyTrans : function(trans, isLoaded){
16497         this.head.removeChild(document.getElementById(trans.scriptId));
16498         clearTimeout(trans.timeoutId);
16499         if(isLoaded){
16500             window[trans.cb] = undefined;
16501             try{
16502                 delete window[trans.cb];
16503             }catch(e){}
16504         }else{
16505             // if hasn't been loaded, wait for load to remove it to prevent script error
16506             window[trans.cb] = function(){
16507                 window[trans.cb] = undefined;
16508                 try{
16509                     delete window[trans.cb];
16510                 }catch(e){}
16511             };
16512         }
16513     },
16514
16515     // private
16516     handleResponse : function(o, trans){
16517         this.trans = false;
16518         this.destroyTrans(trans, true);
16519         var result;
16520         try {
16521             result = trans.reader.readRecords(o);
16522         }catch(e){
16523             this.fireEvent("loadexception", this, o, trans.arg, e);
16524             trans.callback.call(trans.scope||window, null, trans.arg, false);
16525             return;
16526         }
16527         this.fireEvent("load", this, o, trans.arg);
16528         trans.callback.call(trans.scope||window, result, trans.arg, true);
16529     },
16530
16531     // private
16532     handleFailure : function(trans){
16533         this.trans = false;
16534         this.destroyTrans(trans, false);
16535         this.fireEvent("loadexception", this, null, trans.arg);
16536         trans.callback.call(trans.scope||window, null, trans.arg, false);
16537     }
16538 });/*
16539  * Based on:
16540  * Ext JS Library 1.1.1
16541  * Copyright(c) 2006-2007, Ext JS, LLC.
16542  *
16543  * Originally Released Under LGPL - original licence link has changed is not relivant.
16544  *
16545  * Fork - LGPL
16546  * <script type="text/javascript">
16547  */
16548
16549 /**
16550  * @class Roo.data.JsonReader
16551  * @extends Roo.data.DataReader
16552  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16553  * based on mappings in a provided Roo.data.Record constructor.
16554  * 
16555  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16556  * in the reply previously. 
16557  * 
16558  * <p>
16559  * Example code:
16560  * <pre><code>
16561 var RecordDef = Roo.data.Record.create([
16562     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16563     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16564 ]);
16565 var myReader = new Roo.data.JsonReader({
16566     totalProperty: "results",    // The property which contains the total dataset size (optional)
16567     root: "rows",                // The property which contains an Array of row objects
16568     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16569 }, RecordDef);
16570 </code></pre>
16571  * <p>
16572  * This would consume a JSON file like this:
16573  * <pre><code>
16574 { 'results': 2, 'rows': [
16575     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16576     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16577 }
16578 </code></pre>
16579  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16580  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16581  * paged from the remote server.
16582  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16583  * @cfg {String} root name of the property which contains the Array of row objects.
16584  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16585  * @cfg {Array} fields Array of field definition objects
16586  * @constructor
16587  * Create a new JsonReader
16588  * @param {Object} meta Metadata configuration options
16589  * @param {Object} recordType Either an Array of field definition objects,
16590  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16591  */
16592 Roo.data.JsonReader = function(meta, recordType){
16593     
16594     meta = meta || {};
16595     // set some defaults:
16596     Roo.applyIf(meta, {
16597         totalProperty: 'total',
16598         successProperty : 'success',
16599         root : 'data',
16600         id : 'id'
16601     });
16602     
16603     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16604 };
16605 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16606     
16607     readerType : 'Json',
16608     
16609     /**
16610      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16611      * Used by Store query builder to append _requestMeta to params.
16612      * 
16613      */
16614     metaFromRemote : false,
16615     /**
16616      * This method is only used by a DataProxy which has retrieved data from a remote server.
16617      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16618      * @return {Object} data A data block which is used by an Roo.data.Store object as
16619      * a cache of Roo.data.Records.
16620      */
16621     read : function(response){
16622         var json = response.responseText;
16623        
16624         var o = /* eval:var:o */ eval("("+json+")");
16625         if(!o) {
16626             throw {message: "JsonReader.read: Json object not found"};
16627         }
16628         
16629         if(o.metaData){
16630             
16631             delete this.ef;
16632             this.metaFromRemote = true;
16633             this.meta = o.metaData;
16634             this.recordType = Roo.data.Record.create(o.metaData.fields);
16635             this.onMetaChange(this.meta, this.recordType, o);
16636         }
16637         return this.readRecords(o);
16638     },
16639
16640     // private function a store will implement
16641     onMetaChange : function(meta, recordType, o){
16642
16643     },
16644
16645     /**
16646          * @ignore
16647          */
16648     simpleAccess: function(obj, subsc) {
16649         return obj[subsc];
16650     },
16651
16652         /**
16653          * @ignore
16654          */
16655     getJsonAccessor: function(){
16656         var re = /[\[\.]/;
16657         return function(expr) {
16658             try {
16659                 return(re.test(expr))
16660                     ? new Function("obj", "return obj." + expr)
16661                     : function(obj){
16662                         return obj[expr];
16663                     };
16664             } catch(e){}
16665             return Roo.emptyFn;
16666         };
16667     }(),
16668
16669     /**
16670      * Create a data block containing Roo.data.Records from an XML document.
16671      * @param {Object} o An object which contains an Array of row objects in the property specified
16672      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16673      * which contains the total size of the dataset.
16674      * @return {Object} data A data block which is used by an Roo.data.Store object as
16675      * a cache of Roo.data.Records.
16676      */
16677     readRecords : function(o){
16678         /**
16679          * After any data loads, the raw JSON data is available for further custom processing.
16680          * @type Object
16681          */
16682         this.o = o;
16683         var s = this.meta, Record = this.recordType,
16684             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16685
16686 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16687         if (!this.ef) {
16688             if(s.totalProperty) {
16689                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16690                 }
16691                 if(s.successProperty) {
16692                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16693                 }
16694                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16695                 if (s.id) {
16696                         var g = this.getJsonAccessor(s.id);
16697                         this.getId = function(rec) {
16698                                 var r = g(rec);  
16699                                 return (r === undefined || r === "") ? null : r;
16700                         };
16701                 } else {
16702                         this.getId = function(){return null;};
16703                 }
16704             this.ef = [];
16705             for(var jj = 0; jj < fl; jj++){
16706                 f = fi[jj];
16707                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16708                 this.ef[jj] = this.getJsonAccessor(map);
16709             }
16710         }
16711
16712         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16713         if(s.totalProperty){
16714             var vt = parseInt(this.getTotal(o), 10);
16715             if(!isNaN(vt)){
16716                 totalRecords = vt;
16717             }
16718         }
16719         if(s.successProperty){
16720             var vs = this.getSuccess(o);
16721             if(vs === false || vs === 'false'){
16722                 success = false;
16723             }
16724         }
16725         var records = [];
16726         for(var i = 0; i < c; i++){
16727             var n = root[i];
16728             var values = {};
16729             var id = this.getId(n);
16730             for(var j = 0; j < fl; j++){
16731                 f = fi[j];
16732                                 var v = this.ef[j](n);
16733                                 if (!f.convert) {
16734                                         Roo.log('missing convert for ' + f.name);
16735                                         Roo.log(f);
16736                                         continue;
16737                                 }
16738                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16739             }
16740                         if (!Record) {
16741                                 return {
16742                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16743                                         success : false,
16744                                         records : [],
16745                                         totalRecords : 0
16746                                 };
16747                         }
16748             var record = new Record(values, id);
16749             record.json = n;
16750             records[i] = record;
16751         }
16752         return {
16753             raw : o,
16754             success : success,
16755             records : records,
16756             totalRecords : totalRecords
16757         };
16758     },
16759     // used when loading children.. @see loadDataFromChildren
16760     toLoadData: function(rec)
16761     {
16762         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16763         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16764         return { data : data, total : data.length };
16765         
16766     }
16767 });/*
16768  * Based on:
16769  * Ext JS Library 1.1.1
16770  * Copyright(c) 2006-2007, Ext JS, LLC.
16771  *
16772  * Originally Released Under LGPL - original licence link has changed is not relivant.
16773  *
16774  * Fork - LGPL
16775  * <script type="text/javascript">
16776  */
16777
16778 /**
16779  * @class Roo.data.ArrayReader
16780  * @extends Roo.data.DataReader
16781  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16782  * Each element of that Array represents a row of data fields. The
16783  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16784  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16785  * <p>
16786  * Example code:.
16787  * <pre><code>
16788 var RecordDef = Roo.data.Record.create([
16789     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16790     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16791 ]);
16792 var myReader = new Roo.data.ArrayReader({
16793     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16794 }, RecordDef);
16795 </code></pre>
16796  * <p>
16797  * This would consume an Array like this:
16798  * <pre><code>
16799 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16800   </code></pre>
16801  
16802  * @constructor
16803  * Create a new JsonReader
16804  * @param {Object} meta Metadata configuration options.
16805  * @param {Object|Array} recordType Either an Array of field definition objects
16806  * 
16807  * @cfg {Array} fields Array of field definition objects
16808  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16809  * as specified to {@link Roo.data.Record#create},
16810  * or an {@link Roo.data.Record} object
16811  *
16812  * 
16813  * created using {@link Roo.data.Record#create}.
16814  */
16815 Roo.data.ArrayReader = function(meta, recordType)
16816 {    
16817     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16818 };
16819
16820 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16821     
16822       /**
16823      * Create a data block containing Roo.data.Records from an XML document.
16824      * @param {Object} o An Array of row objects which represents the dataset.
16825      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16826      * a cache of Roo.data.Records.
16827      */
16828     readRecords : function(o)
16829     {
16830         var sid = this.meta ? this.meta.id : null;
16831         var recordType = this.recordType, fields = recordType.prototype.fields;
16832         var records = [];
16833         var root = o;
16834         for(var i = 0; i < root.length; i++){
16835             var n = root[i];
16836             var values = {};
16837             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16838             for(var j = 0, jlen = fields.length; j < jlen; j++){
16839                 var f = fields.items[j];
16840                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16841                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16842                 v = f.convert(v);
16843                 values[f.name] = v;
16844             }
16845             var record = new recordType(values, id);
16846             record.json = n;
16847             records[records.length] = record;
16848         }
16849         return {
16850             records : records,
16851             totalRecords : records.length
16852         };
16853     },
16854     // used when loading children.. @see loadDataFromChildren
16855     toLoadData: function(rec)
16856     {
16857         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16858         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16859         
16860     }
16861     
16862     
16863 });/*
16864  * - LGPL
16865  * * 
16866  */
16867
16868 /**
16869  * @class Roo.bootstrap.form.ComboBox
16870  * @extends Roo.bootstrap.form.TriggerField
16871  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16872  * @cfg {Boolean} append (true|false) default false
16873  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16874  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16875  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16876  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16877  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16878  * @cfg {Boolean} animate default true
16879  * @cfg {Boolean} emptyResultText only for touch device
16880  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16881  * @cfg {String} emptyTitle default ''
16882  * @cfg {Number} width fixed with? experimental
16883  * @constructor
16884  * Create a new ComboBox.
16885  * @param {Object} config Configuration options
16886  */
16887 Roo.bootstrap.form.ComboBox = function(config){
16888     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16889     this.addEvents({
16890         /**
16891          * @event expand
16892          * Fires when the dropdown list is expanded
16893         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16894         */
16895         'expand' : true,
16896         /**
16897          * @event collapse
16898          * Fires when the dropdown list is collapsed
16899         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16900         */
16901         'collapse' : true,
16902         /**
16903          * @event beforeselect
16904          * Fires before a list item is selected. Return false to cancel the selection.
16905         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16906         * @param {Roo.data.Record} record The data record returned from the underlying store
16907         * @param {Number} index The index of the selected item in the dropdown list
16908         */
16909         'beforeselect' : true,
16910         /**
16911          * @event select
16912          * Fires when a list item is selected
16913         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16914         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16915         * @param {Number} index The index of the selected item in the dropdown list
16916         */
16917         'select' : true,
16918         /**
16919          * @event beforequery
16920          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16921          * The event object passed has these properties:
16922         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16923         * @param {String} query The query
16924         * @param {Boolean} forceAll true to force "all" query
16925         * @param {Boolean} cancel true to cancel the query
16926         * @param {Object} e The query event object
16927         */
16928         'beforequery': true,
16929          /**
16930          * @event add
16931          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16932         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16933         */
16934         'add' : true,
16935         /**
16936          * @event edit
16937          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16938         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16939         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16940         */
16941         'edit' : true,
16942         /**
16943          * @event remove
16944          * Fires when the remove value from the combobox array
16945         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16946         */
16947         'remove' : true,
16948         /**
16949          * @event afterremove
16950          * Fires when the remove value from the combobox array
16951         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16952         */
16953         'afterremove' : true,
16954         /**
16955          * @event specialfilter
16956          * Fires when specialfilter
16957             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16958             */
16959         'specialfilter' : true,
16960         /**
16961          * @event tick
16962          * Fires when tick the element
16963             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16964             */
16965         'tick' : true,
16966         /**
16967          * @event touchviewdisplay
16968          * Fires when touch view require special display (default is using displayField)
16969             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16970             * @param {Object} cfg set html .
16971             */
16972         'touchviewdisplay' : true
16973         
16974     });
16975     
16976     this.item = [];
16977     this.tickItems = [];
16978     
16979     this.selectedIndex = -1;
16980     if(this.mode == 'local'){
16981         if(config.queryDelay === undefined){
16982             this.queryDelay = 10;
16983         }
16984         if(config.minChars === undefined){
16985             this.minChars = 0;
16986         }
16987     }
16988 };
16989
16990 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16991      
16992     /**
16993      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16994      * rendering into an Roo.Editor, defaults to false)
16995      */
16996     /**
16997      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16998      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16999      */
17000     /**
17001      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
17002      */
17003     /**
17004      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
17005      * the dropdown list (defaults to undefined, with no header element)
17006      */
17007
17008      /**
17009      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
17010      */
17011      
17012      /**
17013      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
17014      */
17015     listWidth: undefined,
17016     /**
17017      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
17018      * mode = 'remote' or 'text' if mode = 'local')
17019      */
17020     displayField: undefined,
17021     
17022     /**
17023      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
17024      * mode = 'remote' or 'value' if mode = 'local'). 
17025      * Note: use of a valueField requires the user make a selection
17026      * in order for a value to be mapped.
17027      */
17028     valueField: undefined,
17029     /**
17030      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17031      */
17032     modalTitle : '',
17033     
17034     /**
17035      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17036      * field's data value (defaults to the underlying DOM element's name)
17037      */
17038     hiddenName: undefined,
17039     /**
17040      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17041      */
17042     listClass: '',
17043     /**
17044      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17045      */
17046     selectedClass: 'active',
17047     
17048     /**
17049      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17050      */
17051     shadow:'sides',
17052     /**
17053      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17054      * anchor positions (defaults to 'tl-bl')
17055      */
17056     listAlign: 'tl-bl?',
17057     /**
17058      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17059      */
17060     maxHeight: 300,
17061     /**
17062      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17063      * query specified by the allQuery config option (defaults to 'query')
17064      */
17065     triggerAction: 'query',
17066     /**
17067      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17068      * (defaults to 4, does not apply if editable = false)
17069      */
17070     minChars : 4,
17071     /**
17072      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17073      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17074      */
17075     typeAhead: false,
17076     /**
17077      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17078      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17079      */
17080     queryDelay: 500,
17081     /**
17082      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17083      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17084      */
17085     pageSize: 0,
17086     /**
17087      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17088      * when editable = true (defaults to false)
17089      */
17090     selectOnFocus:false,
17091     /**
17092      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17093      */
17094     queryParam: 'query',
17095     /**
17096      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17097      * when mode = 'remote' (defaults to 'Loading...')
17098      */
17099     loadingText: 'Loading...',
17100     /**
17101      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17102      */
17103     resizable: false,
17104     /**
17105      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17106      */
17107     handleHeight : 8,
17108     /**
17109      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17110      * traditional select (defaults to true)
17111      */
17112     editable: true,
17113     /**
17114      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17115      */
17116     allQuery: '',
17117     /**
17118      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17119      */
17120     mode: 'remote',
17121     /**
17122      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17123      * listWidth has a higher value)
17124      */
17125     minListWidth : 70,
17126     /**
17127      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17128      * allow the user to set arbitrary text into the field (defaults to false)
17129      */
17130     forceSelection:false,
17131     /**
17132      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17133      * if typeAhead = true (defaults to 250)
17134      */
17135     typeAheadDelay : 250,
17136     /**
17137      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17138      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17139      */
17140     valueNotFoundText : undefined,
17141     /**
17142      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17143      */
17144     blockFocus : false,
17145     
17146     /**
17147      * @cfg {Boolean} disableClear Disable showing of clear button.
17148      */
17149     disableClear : false,
17150     /**
17151      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17152      */
17153     alwaysQuery : false,
17154     
17155     /**
17156      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17157      */
17158     multiple : false,
17159     
17160     /**
17161      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17162      */
17163     invalidClass : "has-warning",
17164     
17165     /**
17166      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17167      */
17168     validClass : "has-success",
17169     
17170     /**
17171      * @cfg {Boolean} specialFilter (true|false) special filter default false
17172      */
17173     specialFilter : false,
17174     
17175     /**
17176      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17177      */
17178     mobileTouchView : true,
17179     
17180     /**
17181      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17182      */
17183     useNativeIOS : false,
17184     
17185     /**
17186      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17187      */
17188     mobile_restrict_height : false,
17189     
17190     ios_options : false,
17191     
17192     //private
17193     addicon : false,
17194     editicon: false,
17195     
17196     page: 0,
17197     hasQuery: false,
17198     append: false,
17199     loadNext: false,
17200     autoFocus : true,
17201     tickable : false,
17202     btnPosition : 'right',
17203     triggerList : true,
17204     showToggleBtn : true,
17205     animate : true,
17206     emptyResultText: 'Empty',
17207     triggerText : 'Select',
17208     emptyTitle : '',
17209     width : false,
17210     
17211     // element that contains real text value.. (when hidden is used..)
17212     
17213     getAutoCreate : function()
17214     {   
17215         var cfg = false;
17216         //render
17217         /*
17218          * Render classic select for iso
17219          */
17220         
17221         if(Roo.isIOS && this.useNativeIOS){
17222             cfg = this.getAutoCreateNativeIOS();
17223             return cfg;
17224         }
17225         
17226         /*
17227          * Touch Devices
17228          */
17229         
17230         if(Roo.isTouch && this.mobileTouchView){
17231             cfg = this.getAutoCreateTouchView();
17232             return cfg;;
17233         }
17234         
17235         /*
17236          *  Normal ComboBox
17237          */
17238         if(!this.tickable){
17239             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17240             return cfg;
17241         }
17242         
17243         /*
17244          *  ComboBox with tickable selections
17245          */
17246              
17247         var align = this.labelAlign || this.parentLabelAlign();
17248         
17249         cfg = {
17250             cls : 'form-group roo-combobox-tickable' //input-group
17251         };
17252         
17253         var btn_text_select = '';
17254         var btn_text_done = '';
17255         var btn_text_cancel = '';
17256         
17257         if (this.btn_text_show) {
17258             btn_text_select = 'Select';
17259             btn_text_done = 'Done';
17260             btn_text_cancel = 'Cancel'; 
17261         }
17262         
17263         var buttons = {
17264             tag : 'div',
17265             cls : 'tickable-buttons',
17266             cn : [
17267                 {
17268                     tag : 'button',
17269                     type : 'button',
17270                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17271                     //html : this.triggerText
17272                     html: btn_text_select
17273                 },
17274                 {
17275                     tag : 'button',
17276                     type : 'button',
17277                     name : 'ok',
17278                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17279                     //html : 'Done'
17280                     html: btn_text_done
17281                 },
17282                 {
17283                     tag : 'button',
17284                     type : 'button',
17285                     name : 'cancel',
17286                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17287                     //html : 'Cancel'
17288                     html: btn_text_cancel
17289                 }
17290             ]
17291         };
17292         
17293         if(this.editable){
17294             buttons.cn.unshift({
17295                 tag: 'input',
17296                 cls: 'roo-select2-search-field-input'
17297             });
17298         }
17299         
17300         var _this = this;
17301         
17302         Roo.each(buttons.cn, function(c){
17303             if (_this.size) {
17304                 c.cls += ' btn-' + _this.size;
17305             }
17306
17307             if (_this.disabled) {
17308                 c.disabled = true;
17309             }
17310         });
17311         
17312         var box = {
17313             tag: 'div',
17314             style : 'display: contents',
17315             cn: [
17316                 {
17317                     tag: 'input',
17318                     type : 'hidden',
17319                     cls: 'form-hidden-field'
17320                 },
17321                 {
17322                     tag: 'ul',
17323                     cls: 'roo-select2-choices',
17324                     cn:[
17325                         {
17326                             tag: 'li',
17327                             cls: 'roo-select2-search-field',
17328                             cn: [
17329                                 buttons
17330                             ]
17331                         }
17332                     ]
17333                 }
17334             ]
17335         };
17336         
17337         var combobox = {
17338             cls: 'roo-select2-container input-group roo-select2-container-multi',
17339             cn: [
17340                 
17341                 box
17342 //                {
17343 //                    tag: 'ul',
17344 //                    cls: 'typeahead typeahead-long dropdown-menu',
17345 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17346 //                }
17347             ]
17348         };
17349         
17350         if(this.hasFeedback && !this.allowBlank){
17351             
17352             var feedback = {
17353                 tag: 'span',
17354                 cls: 'glyphicon form-control-feedback'
17355             };
17356
17357             combobox.cn.push(feedback);
17358         }
17359         
17360         
17361         
17362         var indicator = {
17363             tag : 'i',
17364             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17365             tooltip : 'This field is required'
17366         };
17367         if (Roo.bootstrap.version == 4) {
17368             indicator = {
17369                 tag : 'i',
17370                 style : 'display:none'
17371             };
17372         }
17373         if (align ==='left' && this.fieldLabel.length) {
17374             
17375             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17376             
17377             cfg.cn = [
17378                 indicator,
17379                 {
17380                     tag: 'label',
17381                     'for' :  id,
17382                     cls : 'control-label col-form-label',
17383                     html : this.fieldLabel
17384
17385                 },
17386                 {
17387                     cls : "", 
17388                     cn: [
17389                         combobox
17390                     ]
17391                 }
17392
17393             ];
17394             
17395             var labelCfg = cfg.cn[1];
17396             var contentCfg = cfg.cn[2];
17397             
17398
17399             if(this.indicatorpos == 'right'){
17400                 
17401                 cfg.cn = [
17402                     {
17403                         tag: 'label',
17404                         'for' :  id,
17405                         cls : 'control-label col-form-label',
17406                         cn : [
17407                             {
17408                                 tag : 'span',
17409                                 html : this.fieldLabel
17410                             },
17411                             indicator
17412                         ]
17413                     },
17414                     {
17415                         cls : "",
17416                         cn: [
17417                             combobox
17418                         ]
17419                     }
17420
17421                 ];
17422                 
17423                 
17424                 
17425                 labelCfg = cfg.cn[0];
17426                 contentCfg = cfg.cn[1];
17427             
17428             }
17429             
17430             if(this.labelWidth > 12){
17431                 labelCfg.style = "width: " + this.labelWidth + 'px';
17432             }
17433             if(this.width * 1 > 0){
17434                 contentCfg.style = "width: " + this.width + 'px';
17435             }
17436             if(this.labelWidth < 13 && this.labelmd == 0){
17437                 this.labelmd = this.labelWidth;
17438             }
17439             
17440             if(this.labellg > 0){
17441                 labelCfg.cls += ' col-lg-' + this.labellg;
17442                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17443             }
17444             
17445             if(this.labelmd > 0){
17446                 labelCfg.cls += ' col-md-' + this.labelmd;
17447                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17448             }
17449             
17450             if(this.labelsm > 0){
17451                 labelCfg.cls += ' col-sm-' + this.labelsm;
17452                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17453             }
17454             
17455             if(this.labelxs > 0){
17456                 labelCfg.cls += ' col-xs-' + this.labelxs;
17457                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17458             }
17459                 
17460                 
17461         } else if ( this.fieldLabel.length) {
17462 //                Roo.log(" label");
17463                  cfg.cn = [
17464                    indicator,
17465                     {
17466                         tag: 'label',
17467                         //cls : 'input-group-addon',
17468                         html : this.fieldLabel
17469                     },
17470                     combobox
17471                 ];
17472                 
17473                 if(this.indicatorpos == 'right'){
17474                     cfg.cn = [
17475                         {
17476                             tag: 'label',
17477                             //cls : 'input-group-addon',
17478                             html : this.fieldLabel
17479                         },
17480                         indicator,
17481                         combobox
17482                     ];
17483                     
17484                 }
17485
17486         } else {
17487             
17488 //                Roo.log(" no label && no align");
17489                 cfg = combobox
17490                      
17491                 
17492         }
17493          
17494         var settings=this;
17495         ['xs','sm','md','lg'].map(function(size){
17496             if (settings[size]) {
17497                 cfg.cls += ' col-' + size + '-' + settings[size];
17498             }
17499         });
17500         
17501         return cfg;
17502         
17503     },
17504     
17505     _initEventsCalled : false,
17506     
17507     // private
17508     initEvents: function()
17509     {   
17510         if (this._initEventsCalled) { // as we call render... prevent looping...
17511             return;
17512         }
17513         this._initEventsCalled = true;
17514         
17515         if (!this.store) {
17516             throw "can not find store for combo";
17517         }
17518         
17519         this.indicator = this.indicatorEl();
17520         
17521         this.store = Roo.factory(this.store, Roo.data);
17522         this.store.parent = this;
17523         
17524         // if we are building from html. then this element is so complex, that we can not really
17525         // use the rendered HTML.
17526         // so we have to trash and replace the previous code.
17527         if (Roo.XComponent.build_from_html) {
17528             // remove this element....
17529             var e = this.el.dom, k=0;
17530             while (e ) { e = e.previousSibling;  ++k;}
17531
17532             this.el.remove();
17533             
17534             this.el=false;
17535             this.rendered = false;
17536             
17537             this.render(this.parent().getChildContainer(true), k);
17538         }
17539         
17540         if(Roo.isIOS && this.useNativeIOS){
17541             this.initIOSView();
17542             return;
17543         }
17544         
17545         /*
17546          * Touch Devices
17547          */
17548         
17549         if(Roo.isTouch && this.mobileTouchView){
17550             this.initTouchView();
17551             return;
17552         }
17553         
17554         if(this.tickable){
17555             this.initTickableEvents();
17556             return;
17557         }
17558         
17559         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17560         
17561         if(this.hiddenName){
17562             
17563             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17564             
17565             this.hiddenField.dom.value =
17566                 this.hiddenValue !== undefined ? this.hiddenValue :
17567                 this.value !== undefined ? this.value : '';
17568
17569             // prevent input submission
17570             this.el.dom.removeAttribute('name');
17571             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17572              
17573              
17574         }
17575         //if(Roo.isGecko){
17576         //    this.el.dom.setAttribute('autocomplete', 'off');
17577         //}
17578         
17579         var cls = 'x-combo-list';
17580         
17581         //this.list = new Roo.Layer({
17582         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17583         //});
17584         
17585         var _this = this;
17586         
17587         (function(){
17588             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17589             _this.list.setWidth(lw);
17590         }).defer(100);
17591         
17592         this.list.on('mouseover', this.onViewOver, this);
17593         this.list.on('mousemove', this.onViewMove, this);
17594         this.list.on('scroll', this.onViewScroll, this);
17595         
17596         /*
17597         this.list.swallowEvent('mousewheel');
17598         this.assetHeight = 0;
17599
17600         if(this.title){
17601             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17602             this.assetHeight += this.header.getHeight();
17603         }
17604
17605         this.innerList = this.list.createChild({cls:cls+'-inner'});
17606         this.innerList.on('mouseover', this.onViewOver, this);
17607         this.innerList.on('mousemove', this.onViewMove, this);
17608         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17609         
17610         if(this.allowBlank && !this.pageSize && !this.disableClear){
17611             this.footer = this.list.createChild({cls:cls+'-ft'});
17612             this.pageTb = new Roo.Toolbar(this.footer);
17613            
17614         }
17615         if(this.pageSize){
17616             this.footer = this.list.createChild({cls:cls+'-ft'});
17617             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17618                     {pageSize: this.pageSize});
17619             
17620         }
17621         
17622         if (this.pageTb && this.allowBlank && !this.disableClear) {
17623             var _this = this;
17624             this.pageTb.add(new Roo.Toolbar.Fill(), {
17625                 cls: 'x-btn-icon x-btn-clear',
17626                 text: '&#160;',
17627                 handler: function()
17628                 {
17629                     _this.collapse();
17630                     _this.clearValue();
17631                     _this.onSelect(false, -1);
17632                 }
17633             });
17634         }
17635         if (this.footer) {
17636             this.assetHeight += this.footer.getHeight();
17637         }
17638         */
17639             
17640         if(!this.tpl){
17641             this.tpl = Roo.bootstrap.version == 4 ?
17642                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17643                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17644         }
17645
17646         this.view = new Roo.View(this.list, this.tpl, {
17647             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17648         });
17649         //this.view.wrapEl.setDisplayed(false);
17650         this.view.on('click', this.onViewClick, this);
17651         
17652         
17653         this.store.on('beforeload', this.onBeforeLoad, this);
17654         this.store.on('load', this.onLoad, this);
17655         this.store.on('loadexception', this.onLoadException, this);
17656         /*
17657         if(this.resizable){
17658             this.resizer = new Roo.Resizable(this.list,  {
17659                pinned:true, handles:'se'
17660             });
17661             this.resizer.on('resize', function(r, w, h){
17662                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17663                 this.listWidth = w;
17664                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17665                 this.restrictHeight();
17666             }, this);
17667             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17668         }
17669         */
17670         if(!this.editable){
17671             this.editable = true;
17672             this.setEditable(false);
17673         }
17674         
17675         /*
17676         
17677         if (typeof(this.events.add.listeners) != 'undefined') {
17678             
17679             this.addicon = this.wrap.createChild(
17680                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17681        
17682             this.addicon.on('click', function(e) {
17683                 this.fireEvent('add', this);
17684             }, this);
17685         }
17686         if (typeof(this.events.edit.listeners) != 'undefined') {
17687             
17688             this.editicon = this.wrap.createChild(
17689                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17690             if (this.addicon) {
17691                 this.editicon.setStyle('margin-left', '40px');
17692             }
17693             this.editicon.on('click', function(e) {
17694                 
17695                 // we fire even  if inothing is selected..
17696                 this.fireEvent('edit', this, this.lastData );
17697                 
17698             }, this);
17699         }
17700         */
17701         
17702         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17703             "up" : function(e){
17704                 this.inKeyMode = true;
17705                 this.selectPrev();
17706             },
17707
17708             "down" : function(e){
17709                 if(!this.isExpanded()){
17710                     this.onTriggerClick();
17711                 }else{
17712                     this.inKeyMode = true;
17713                     this.selectNext();
17714                 }
17715             },
17716
17717             "enter" : function(e){
17718 //                this.onViewClick();
17719                 //return true;
17720                 this.collapse();
17721                 
17722                 if(this.fireEvent("specialkey", this, e)){
17723                     this.onViewClick(false);
17724                 }
17725                 
17726                 return true;
17727             },
17728
17729             "esc" : function(e){
17730                 this.collapse();
17731             },
17732
17733             "tab" : function(e){
17734                 this.collapse();
17735                 
17736                 if(this.fireEvent("specialkey", this, e)){
17737                     this.onViewClick(false);
17738                 }
17739                 
17740                 return true;
17741             },
17742
17743             scope : this,
17744
17745             doRelay : function(foo, bar, hname){
17746                 if(hname == 'down' || this.scope.isExpanded()){
17747                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17748                 }
17749                 return true;
17750             },
17751
17752             forceKeyDown: true
17753         });
17754         
17755         
17756         this.queryDelay = Math.max(this.queryDelay || 10,
17757                 this.mode == 'local' ? 10 : 250);
17758         
17759         
17760         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17761         
17762         if(this.typeAhead){
17763             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17764         }
17765         if(this.editable !== false){
17766             this.inputEl().on("keyup", this.onKeyUp, this);
17767         }
17768         if(this.forceSelection){
17769             this.inputEl().on('blur', this.doForce, this);
17770         }
17771         
17772         if(this.multiple){
17773             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17774             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17775         }
17776     },
17777     
17778     initTickableEvents: function()
17779     {   
17780         this.createList();
17781         
17782         if(this.hiddenName){
17783             
17784             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17785             
17786             this.hiddenField.dom.value =
17787                 this.hiddenValue !== undefined ? this.hiddenValue :
17788                 this.value !== undefined ? this.value : '';
17789
17790             // prevent input submission
17791             this.el.dom.removeAttribute('name');
17792             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17793              
17794              
17795         }
17796         
17797 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17798         
17799         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17800         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17801         if(this.triggerList){
17802             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17803         }
17804          
17805         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17806         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17807         
17808         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17809         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17810         
17811         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17812         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17813         
17814         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17815         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17816         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17817         
17818         this.okBtn.hide();
17819         this.cancelBtn.hide();
17820         
17821         var _this = this;
17822         
17823         (function(){
17824             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17825             _this.list.setWidth(lw);
17826         }).defer(100);
17827         
17828         this.list.on('mouseover', this.onViewOver, this);
17829         this.list.on('mousemove', this.onViewMove, this);
17830         
17831         this.list.on('scroll', this.onViewScroll, this);
17832         
17833         if(!this.tpl){
17834             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17835                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17836         }
17837
17838         this.view = new Roo.View(this.list, this.tpl, {
17839             singleSelect:true,
17840             tickable:true,
17841             parent:this,
17842             store: this.store,
17843             selectedClass: this.selectedClass
17844         });
17845         
17846         //this.view.wrapEl.setDisplayed(false);
17847         this.view.on('click', this.onViewClick, this);
17848         
17849         
17850         
17851         this.store.on('beforeload', this.onBeforeLoad, this);
17852         this.store.on('load', this.onLoad, this);
17853         this.store.on('loadexception', this.onLoadException, this);
17854         
17855         if(this.editable){
17856             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17857                 "up" : function(e){
17858                     this.inKeyMode = true;
17859                     this.selectPrev();
17860                 },
17861
17862                 "down" : function(e){
17863                     this.inKeyMode = true;
17864                     this.selectNext();
17865                 },
17866
17867                 "enter" : function(e){
17868                     if(this.fireEvent("specialkey", this, e)){
17869                         this.onViewClick(false);
17870                     }
17871                     
17872                     return true;
17873                 },
17874
17875                 "esc" : function(e){
17876                     this.onTickableFooterButtonClick(e, false, false);
17877                 },
17878
17879                 "tab" : function(e){
17880                     this.fireEvent("specialkey", this, e);
17881                     
17882                     this.onTickableFooterButtonClick(e, false, false);
17883                     
17884                     return true;
17885                 },
17886
17887                 scope : this,
17888
17889                 doRelay : function(e, fn, key){
17890                     if(this.scope.isExpanded()){
17891                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17892                     }
17893                     return true;
17894                 },
17895
17896                 forceKeyDown: true
17897             });
17898         }
17899         
17900         this.queryDelay = Math.max(this.queryDelay || 10,
17901                 this.mode == 'local' ? 10 : 250);
17902         
17903         
17904         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17905         
17906         if(this.typeAhead){
17907             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17908         }
17909         
17910         if(this.editable !== false){
17911             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17912         }
17913         
17914         this.indicator = this.indicatorEl();
17915         
17916         if(this.indicator){
17917             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17918             this.indicator.hide();
17919         }
17920         
17921     },
17922
17923     onDestroy : function(){
17924         if(this.view){
17925             this.view.setStore(null);
17926             this.view.el.removeAllListeners();
17927             this.view.el.remove();
17928             this.view.purgeListeners();
17929         }
17930         if(this.list){
17931             this.list.dom.innerHTML  = '';
17932         }
17933         
17934         if(this.store){
17935             this.store.un('beforeload', this.onBeforeLoad, this);
17936             this.store.un('load', this.onLoad, this);
17937             this.store.un('loadexception', this.onLoadException, this);
17938         }
17939         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17940     },
17941
17942     // private
17943     fireKey : function(e){
17944         if(e.isNavKeyPress() && !this.list.isVisible()){
17945             this.fireEvent("specialkey", this, e);
17946         }
17947     },
17948
17949     // private
17950     onResize: function(w, h)
17951     {
17952         
17953         
17954 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17955 //        
17956 //        if(typeof w != 'number'){
17957 //            // we do not handle it!?!?
17958 //            return;
17959 //        }
17960 //        var tw = this.trigger.getWidth();
17961 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17962 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17963 //        var x = w - tw;
17964 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17965 //            
17966 //        //this.trigger.setStyle('left', x+'px');
17967 //        
17968 //        if(this.list && this.listWidth === undefined){
17969 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17970 //            this.list.setWidth(lw);
17971 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17972 //        }
17973         
17974     
17975         
17976     },
17977
17978     /**
17979      * Allow or prevent the user from directly editing the field text.  If false is passed,
17980      * the user will only be able to select from the items defined in the dropdown list.  This method
17981      * is the runtime equivalent of setting the 'editable' config option at config time.
17982      * @param {Boolean} value True to allow the user to directly edit the field text
17983      */
17984     setEditable : function(value){
17985         if(value == this.editable){
17986             return;
17987         }
17988         this.editable = value;
17989         if(!value){
17990             this.inputEl().dom.setAttribute('readOnly', true);
17991             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17992             this.inputEl().addClass('x-combo-noedit');
17993         }else{
17994             this.inputEl().dom.removeAttribute('readOnly');
17995             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17996             this.inputEl().removeClass('x-combo-noedit');
17997         }
17998     },
17999
18000     // private
18001     
18002     onBeforeLoad : function(combo,opts){
18003         if(!this.hasFocus){
18004             return;
18005         }
18006          if (!opts.add) {
18007             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
18008          }
18009         this.restrictHeight();
18010         this.selectedIndex = -1;
18011     },
18012
18013     // private
18014     onLoad : function(){
18015         
18016         this.hasQuery = false;
18017         
18018         if(!this.hasFocus){
18019             return;
18020         }
18021         
18022         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18023             this.loading.hide();
18024         }
18025         
18026         if(this.store.getCount() > 0){
18027             
18028             this.expand();
18029             this.restrictHeight();
18030             if(this.lastQuery == this.allQuery){
18031                 if(this.editable && !this.tickable){
18032                     this.inputEl().dom.select();
18033                 }
18034                 
18035                 if(
18036                     !this.selectByValue(this.value, true) &&
18037                     this.autoFocus && 
18038                     (
18039                         !this.store.lastOptions ||
18040                         typeof(this.store.lastOptions.add) == 'undefined' || 
18041                         this.store.lastOptions.add != true
18042                     )
18043                 ){
18044                     this.select(0, true);
18045                 }
18046             }else{
18047                 if(this.autoFocus){
18048                     this.selectNext();
18049                 }
18050                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18051                     this.taTask.delay(this.typeAheadDelay);
18052                 }
18053             }
18054         }else{
18055             this.onEmptyResults();
18056         }
18057         
18058         //this.el.focus();
18059     },
18060     // private
18061     onLoadException : function()
18062     {
18063         this.hasQuery = false;
18064         
18065         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18066             this.loading.hide();
18067         }
18068         
18069         if(this.tickable && this.editable){
18070             return;
18071         }
18072         
18073         this.collapse();
18074         // only causes errors at present
18075         //Roo.log(this.store.reader.jsonData);
18076         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18077             // fixme
18078             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18079         //}
18080         
18081         
18082     },
18083     // private
18084     onTypeAhead : function(){
18085         if(this.store.getCount() > 0){
18086             var r = this.store.getAt(0);
18087             var newValue = r.data[this.displayField];
18088             var len = newValue.length;
18089             var selStart = this.getRawValue().length;
18090             
18091             if(selStart != len){
18092                 this.setRawValue(newValue);
18093                 this.selectText(selStart, newValue.length);
18094             }
18095         }
18096     },
18097
18098     // private
18099     onSelect : function(record, index){
18100         
18101         if(this.fireEvent('beforeselect', this, record, index) !== false){
18102         
18103             this.setFromData(index > -1 ? record.data : false);
18104             
18105             this.collapse();
18106             this.fireEvent('select', this, record, index);
18107         }
18108     },
18109
18110     /**
18111      * Returns the currently selected field value or empty string if no value is set.
18112      * @return {String} value The selected value
18113      */
18114     getValue : function()
18115     {
18116         if(Roo.isIOS && this.useNativeIOS){
18117             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18118         }
18119         
18120         if(this.multiple){
18121             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18122         }
18123         
18124         if(this.valueField){
18125             return typeof this.value != 'undefined' ? this.value : '';
18126         }else{
18127             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18128         }
18129     },
18130     
18131     getRawValue : function()
18132     {
18133         if(Roo.isIOS && this.useNativeIOS){
18134             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18135         }
18136         
18137         var v = this.inputEl().getValue();
18138         
18139         return v;
18140     },
18141
18142     /**
18143      * Clears any text/value currently set in the field
18144      */
18145     clearValue : function(){
18146         
18147         if(this.hiddenField){
18148             this.hiddenField.dom.value = '';
18149         }
18150         this.value = '';
18151         this.setRawValue('');
18152         this.lastSelectionText = '';
18153         this.lastData = false;
18154         
18155         var close = this.closeTriggerEl();
18156         
18157         if(close){
18158             close.hide();
18159         }
18160         
18161         this.validate();
18162         
18163     },
18164
18165     /**
18166      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18167      * will be displayed in the field.  If the value does not match the data value of an existing item,
18168      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18169      * Otherwise the field will be blank (although the value will still be set).
18170      * @param {String} value The value to match
18171      */
18172     setValue : function(v)
18173     {
18174         if(Roo.isIOS && this.useNativeIOS){
18175             this.setIOSValue(v);
18176             return;
18177         }
18178         
18179         if(this.multiple){
18180             this.syncValue();
18181             return;
18182         }
18183         
18184         var text = v;
18185         if(this.valueField){
18186             var r = this.findRecord(this.valueField, v);
18187             if(r){
18188                 text = r.data[this.displayField];
18189             }else if(this.valueNotFoundText !== undefined){
18190                 text = this.valueNotFoundText;
18191             }
18192         }
18193         this.lastSelectionText = text;
18194         if(this.hiddenField){
18195             this.hiddenField.dom.value = v;
18196         }
18197         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18198         this.value = v;
18199         
18200         var close = this.closeTriggerEl();
18201         
18202         if(close){
18203             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18204         }
18205         
18206         this.validate();
18207     },
18208     /**
18209      * @property {Object} the last set data for the element
18210      */
18211     
18212     lastData : false,
18213     /**
18214      * Sets the value of the field based on a object which is related to the record format for the store.
18215      * @param {Object} value the value to set as. or false on reset?
18216      */
18217     setFromData : function(o){
18218         
18219         if(this.multiple){
18220             this.addItem(o);
18221             return;
18222         }
18223             
18224         var dv = ''; // display value
18225         var vv = ''; // value value..
18226         this.lastData = o;
18227         if (this.displayField) {
18228             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18229         } else {
18230             // this is an error condition!!!
18231             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18232         }
18233         
18234         if(this.valueField){
18235             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18236         }
18237         
18238         var close = this.closeTriggerEl();
18239         
18240         if(close){
18241             if(dv.length || vv * 1 > 0){
18242                 close.show() ;
18243                 this.blockFocus=true;
18244             } else {
18245                 close.hide();
18246             }             
18247         }
18248         
18249         if(this.hiddenField){
18250             this.hiddenField.dom.value = vv;
18251             
18252             this.lastSelectionText = dv;
18253             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18254             this.value = vv;
18255             return;
18256         }
18257         // no hidden field.. - we store the value in 'value', but still display
18258         // display field!!!!
18259         this.lastSelectionText = dv;
18260         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18261         this.value = vv;
18262         
18263         
18264         
18265     },
18266     // private
18267     reset : function(){
18268         // overridden so that last data is reset..
18269         
18270         if(this.multiple){
18271             this.clearItem();
18272             return;
18273         }
18274         
18275         this.setValue(this.originalValue);
18276         //this.clearInvalid();
18277         this.lastData = false;
18278         if (this.view) {
18279             this.view.clearSelections();
18280         }
18281         
18282         this.validate();
18283     },
18284     // private
18285     findRecord : function(prop, value){
18286         var record;
18287         if(this.store.getCount() > 0){
18288             this.store.each(function(r){
18289                 if(r.data[prop] == value){
18290                     record = r;
18291                     return false;
18292                 }
18293                 return true;
18294             });
18295         }
18296         return record;
18297     },
18298     
18299     getName: function()
18300     {
18301         // returns hidden if it's set..
18302         if (!this.rendered) {return ''};
18303         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18304         
18305     },
18306     // private
18307     onViewMove : function(e, t){
18308         this.inKeyMode = false;
18309     },
18310
18311     // private
18312     onViewOver : function(e, t){
18313         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18314             return;
18315         }
18316         var item = this.view.findItemFromChild(t);
18317         
18318         if(item){
18319             var index = this.view.indexOf(item);
18320             this.select(index, false);
18321         }
18322     },
18323
18324     // private
18325     onViewClick : function(view, doFocus, el, e)
18326     {
18327         var index = this.view.getSelectedIndexes()[0];
18328         
18329         var r = this.store.getAt(index);
18330         
18331         if(this.tickable){
18332             
18333             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18334                 return;
18335             }
18336             
18337             var rm = false;
18338             var _this = this;
18339             
18340             Roo.each(this.tickItems, function(v,k){
18341                 
18342                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18343                     Roo.log(v);
18344                     _this.tickItems.splice(k, 1);
18345                     
18346                     if(typeof(e) == 'undefined' && view == false){
18347                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18348                     }
18349                     
18350                     rm = true;
18351                     return;
18352                 }
18353             });
18354             
18355             if(rm){
18356                 return;
18357             }
18358             
18359             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18360                 this.tickItems.push(r.data);
18361             }
18362             
18363             if(typeof(e) == 'undefined' && view == false){
18364                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18365             }
18366                     
18367             return;
18368         }
18369         
18370         if(r){
18371             this.onSelect(r, index);
18372         }
18373         if(doFocus !== false && !this.blockFocus){
18374             this.inputEl().focus();
18375         }
18376     },
18377
18378     // private
18379     restrictHeight : function(){
18380         //this.innerList.dom.style.height = '';
18381         //var inner = this.innerList.dom;
18382         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18383         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18384         //this.list.beginUpdate();
18385         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18386         this.list.alignTo(this.inputEl(), this.listAlign);
18387         this.list.alignTo(this.inputEl(), this.listAlign);
18388         //this.list.endUpdate();
18389     },
18390
18391     // private
18392     onEmptyResults : function(){
18393         
18394         if(this.tickable && this.editable){
18395             this.hasFocus = false;
18396             this.restrictHeight();
18397             return;
18398         }
18399         
18400         this.collapse();
18401     },
18402
18403     /**
18404      * Returns true if the dropdown list is expanded, else false.
18405      */
18406     isExpanded : function(){
18407         return this.list.isVisible();
18408     },
18409
18410     /**
18411      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18412      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18413      * @param {String} value The data value of the item to select
18414      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18415      * selected item if it is not currently in view (defaults to true)
18416      * @return {Boolean} True if the value matched an item in the list, else false
18417      */
18418     selectByValue : function(v, scrollIntoView){
18419         if(v !== undefined && v !== null){
18420             var r = this.findRecord(this.valueField || this.displayField, v);
18421             if(r){
18422                 this.select(this.store.indexOf(r), scrollIntoView);
18423                 return true;
18424             }
18425         }
18426         return false;
18427     },
18428
18429     /**
18430      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18431      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18432      * @param {Number} index The zero-based index of the list item to select
18433      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18434      * selected item if it is not currently in view (defaults to true)
18435      */
18436     select : function(index, scrollIntoView){
18437         this.selectedIndex = index;
18438         this.view.select(index);
18439         if(scrollIntoView !== false){
18440             var el = this.view.getNode(index);
18441             /*
18442              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18443              */
18444             if(el){
18445                 this.list.scrollChildIntoView(el, false);
18446             }
18447         }
18448     },
18449
18450     // private
18451     selectNext : function(){
18452         var ct = this.store.getCount();
18453         if(ct > 0){
18454             if(this.selectedIndex == -1){
18455                 this.select(0);
18456             }else if(this.selectedIndex < ct-1){
18457                 this.select(this.selectedIndex+1);
18458             }
18459         }
18460     },
18461
18462     // private
18463     selectPrev : function(){
18464         var ct = this.store.getCount();
18465         if(ct > 0){
18466             if(this.selectedIndex == -1){
18467                 this.select(0);
18468             }else if(this.selectedIndex != 0){
18469                 this.select(this.selectedIndex-1);
18470             }
18471         }
18472     },
18473
18474     // private
18475     onKeyUp : function(e){
18476         if(this.editable !== false && !e.isSpecialKey()){
18477             this.lastKey = e.getKey();
18478             this.dqTask.delay(this.queryDelay);
18479         }
18480     },
18481
18482     // private
18483     validateBlur : function(){
18484         return !this.list || !this.list.isVisible();   
18485     },
18486
18487     // private
18488     initQuery : function(){
18489         
18490         var v = this.getRawValue();
18491         
18492         if(this.tickable && this.editable){
18493             v = this.tickableInputEl().getValue();
18494         }
18495         
18496         this.doQuery(v);
18497     },
18498
18499     // private
18500     doForce : function(){
18501         if(this.inputEl().dom.value.length > 0){
18502             this.inputEl().dom.value =
18503                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18504              
18505         }
18506     },
18507
18508     /**
18509      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18510      * query allowing the query action to be canceled if needed.
18511      * @param {String} query The SQL query to execute
18512      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18513      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18514      * saved in the current store (defaults to false)
18515      */
18516     doQuery : function(q, forceAll){
18517         
18518         if(q === undefined || q === null){
18519             q = '';
18520         }
18521         var qe = {
18522             query: q,
18523             forceAll: forceAll,
18524             combo: this,
18525             cancel:false
18526         };
18527         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18528             return false;
18529         }
18530         q = qe.query;
18531         
18532         forceAll = qe.forceAll;
18533         if(forceAll === true || (q.length >= this.minChars)){
18534             
18535             this.hasQuery = true;
18536             
18537             if(this.lastQuery != q || this.alwaysQuery){
18538                 this.lastQuery = q;
18539                 if(this.mode == 'local'){
18540                     this.selectedIndex = -1;
18541                     if(forceAll){
18542                         this.store.clearFilter();
18543                     }else{
18544                         
18545                         if(this.specialFilter){
18546                             this.fireEvent('specialfilter', this);
18547                             this.onLoad();
18548                             return;
18549                         }
18550                         
18551                         this.store.filter(this.displayField, q);
18552                     }
18553                     
18554                     this.store.fireEvent("datachanged", this.store);
18555                     
18556                     this.onLoad();
18557                     
18558                     
18559                 }else{
18560                     
18561                     this.store.baseParams[this.queryParam] = q;
18562                     
18563                     var options = {params : this.getParams(q)};
18564                     
18565                     if(this.loadNext){
18566                         options.add = true;
18567                         options.params.start = this.page * this.pageSize;
18568                     }
18569                     
18570                     this.store.load(options);
18571                     
18572                     /*
18573                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18574                      *  we should expand the list on onLoad
18575                      *  so command out it
18576                      */
18577 //                    this.expand();
18578                 }
18579             }else{
18580                 this.selectedIndex = -1;
18581                 this.onLoad();   
18582             }
18583         }
18584         
18585         this.loadNext = false;
18586     },
18587     
18588     // private
18589     getParams : function(q){
18590         var p = {};
18591         //p[this.queryParam] = q;
18592         
18593         if(this.pageSize){
18594             p.start = 0;
18595             p.limit = this.pageSize;
18596         }
18597         return p;
18598     },
18599
18600     /**
18601      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18602      */
18603     collapse : function(){
18604         if(!this.isExpanded()){
18605             return;
18606         }
18607         
18608         this.list.hide();
18609         
18610         this.hasFocus = false;
18611         
18612         if(this.tickable){
18613             this.okBtn.hide();
18614             this.cancelBtn.hide();
18615             this.trigger.show();
18616             
18617             if(this.editable){
18618                 this.tickableInputEl().dom.value = '';
18619                 this.tickableInputEl().blur();
18620             }
18621             
18622         }
18623         
18624         Roo.get(document).un('mousedown', this.collapseIf, this);
18625         Roo.get(document).un('mousewheel', this.collapseIf, this);
18626         if (!this.editable) {
18627             Roo.get(document).un('keydown', this.listKeyPress, this);
18628         }
18629         this.fireEvent('collapse', this);
18630         
18631         this.validate();
18632     },
18633
18634     // private
18635     collapseIf : function(e){
18636         var in_combo  = e.within(this.el);
18637         var in_list =  e.within(this.list);
18638         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18639         
18640         if (in_combo || in_list || is_list) {
18641             //e.stopPropagation();
18642             return;
18643         }
18644         
18645         if(this.tickable){
18646             this.onTickableFooterButtonClick(e, false, false);
18647         }
18648
18649         this.collapse();
18650         
18651     },
18652
18653     /**
18654      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18655      */
18656     expand : function(){
18657        
18658         if(this.isExpanded() || !this.hasFocus){
18659             return;
18660         }
18661         
18662         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18663         this.list.setWidth(lw);
18664         
18665         Roo.log('expand');
18666         
18667         this.list.show();
18668         
18669         this.restrictHeight();
18670         
18671         if(this.tickable){
18672             
18673             this.tickItems = Roo.apply([], this.item);
18674             
18675             this.okBtn.show();
18676             this.cancelBtn.show();
18677             this.trigger.hide();
18678             
18679             if(this.editable){
18680                 this.tickableInputEl().focus();
18681             }
18682             
18683         }
18684         
18685         Roo.get(document).on('mousedown', this.collapseIf, this);
18686         Roo.get(document).on('mousewheel', this.collapseIf, this);
18687         if (!this.editable) {
18688             Roo.get(document).on('keydown', this.listKeyPress, this);
18689         }
18690         
18691         this.fireEvent('expand', this);
18692     },
18693
18694     // private
18695     // Implements the default empty TriggerField.onTriggerClick function
18696     onTriggerClick : function(e)
18697     {
18698         Roo.log('trigger click');
18699         
18700         if(this.disabled || !this.triggerList){
18701             return;
18702         }
18703         
18704         this.page = 0;
18705         this.loadNext = false;
18706         
18707         if(this.isExpanded()){
18708             this.collapse();
18709             if (!this.blockFocus) {
18710                 this.inputEl().focus();
18711             }
18712             
18713         }else {
18714             this.hasFocus = true;
18715             if(this.triggerAction == 'all') {
18716                 this.doQuery(this.allQuery, true);
18717             } else {
18718                 this.doQuery(this.getRawValue());
18719             }
18720             if (!this.blockFocus) {
18721                 this.inputEl().focus();
18722             }
18723         }
18724     },
18725     
18726     onTickableTriggerClick : function(e)
18727     {
18728         if(this.disabled){
18729             return;
18730         }
18731         
18732         this.page = 0;
18733         this.loadNext = false;
18734         this.hasFocus = true;
18735         
18736         if(this.triggerAction == 'all') {
18737             this.doQuery(this.allQuery, true);
18738         } else {
18739             this.doQuery(this.getRawValue());
18740         }
18741     },
18742     
18743     onSearchFieldClick : function(e)
18744     {
18745         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18746             this.onTickableFooterButtonClick(e, false, false);
18747             return;
18748         }
18749         
18750         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18751             return;
18752         }
18753         
18754         this.page = 0;
18755         this.loadNext = false;
18756         this.hasFocus = true;
18757         
18758         if(this.triggerAction == 'all') {
18759             this.doQuery(this.allQuery, true);
18760         } else {
18761             this.doQuery(this.getRawValue());
18762         }
18763     },
18764     
18765     listKeyPress : function(e)
18766     {
18767         //Roo.log('listkeypress');
18768         // scroll to first matching element based on key pres..
18769         if (e.isSpecialKey()) {
18770             return false;
18771         }
18772         var k = String.fromCharCode(e.getKey()).toUpperCase();
18773         //Roo.log(k);
18774         var match  = false;
18775         var csel = this.view.getSelectedNodes();
18776         var cselitem = false;
18777         if (csel.length) {
18778             var ix = this.view.indexOf(csel[0]);
18779             cselitem  = this.store.getAt(ix);
18780             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18781                 cselitem = false;
18782             }
18783             
18784         }
18785         
18786         this.store.each(function(v) { 
18787             if (cselitem) {
18788                 // start at existing selection.
18789                 if (cselitem.id == v.id) {
18790                     cselitem = false;
18791                 }
18792                 return true;
18793             }
18794                 
18795             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18796                 match = this.store.indexOf(v);
18797                 return false;
18798             }
18799             return true;
18800         }, this);
18801         
18802         if (match === false) {
18803             return true; // no more action?
18804         }
18805         // scroll to?
18806         this.view.select(match);
18807         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18808         sn.scrollIntoView(sn.dom.parentNode, false);
18809     },
18810     
18811     onViewScroll : function(e, t){
18812         
18813         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){
18814             return;
18815         }
18816         
18817         this.hasQuery = true;
18818         
18819         this.loading = this.list.select('.loading', true).first();
18820         
18821         if(this.loading === null){
18822             this.list.createChild({
18823                 tag: 'div',
18824                 cls: 'loading roo-select2-more-results roo-select2-active',
18825                 html: 'Loading more results...'
18826             });
18827             
18828             this.loading = this.list.select('.loading', true).first();
18829             
18830             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18831             
18832             this.loading.hide();
18833         }
18834         
18835         this.loading.show();
18836         
18837         var _combo = this;
18838         
18839         this.page++;
18840         this.loadNext = true;
18841         
18842         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18843         
18844         return;
18845     },
18846     
18847     addItem : function(o)
18848     {   
18849         var dv = ''; // display value
18850         
18851         if (this.displayField) {
18852             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18853         } else {
18854             // this is an error condition!!!
18855             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18856         }
18857         
18858         if(!dv.length){
18859             return;
18860         }
18861         
18862         var choice = this.choices.createChild({
18863             tag: 'li',
18864             cls: 'roo-select2-search-choice',
18865             cn: [
18866                 {
18867                     tag: 'div',
18868                     html: dv
18869                 },
18870                 {
18871                     tag: 'a',
18872                     href: '#',
18873                     cls: 'roo-select2-search-choice-close fa fa-times',
18874                     tabindex: '-1'
18875                 }
18876             ]
18877             
18878         }, this.searchField);
18879         
18880         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18881         
18882         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18883         
18884         this.item.push(o);
18885         
18886         this.lastData = o;
18887         
18888         this.syncValue();
18889         
18890         this.inputEl().dom.value = '';
18891         
18892         this.validate();
18893     },
18894     
18895     onRemoveItem : function(e, _self, o)
18896     {
18897         e.preventDefault();
18898         
18899         this.lastItem = Roo.apply([], this.item);
18900         
18901         var index = this.item.indexOf(o.data) * 1;
18902         
18903         if( index < 0){
18904             Roo.log('not this item?!');
18905             return;
18906         }
18907         
18908         this.item.splice(index, 1);
18909         o.item.remove();
18910         
18911         this.syncValue();
18912         
18913         this.fireEvent('remove', this, e);
18914         
18915         this.validate();
18916         
18917     },
18918     
18919     syncValue : function()
18920     {
18921         if(!this.item.length){
18922             this.clearValue();
18923             return;
18924         }
18925             
18926         var value = [];
18927         var _this = this;
18928         Roo.each(this.item, function(i){
18929             if(_this.valueField){
18930                 value.push(i[_this.valueField]);
18931                 return;
18932             }
18933
18934             value.push(i);
18935         });
18936
18937         this.value = value.join(',');
18938
18939         if(this.hiddenField){
18940             this.hiddenField.dom.value = this.value;
18941         }
18942         
18943         this.store.fireEvent("datachanged", this.store);
18944         
18945         this.validate();
18946     },
18947     
18948     clearItem : function()
18949     {
18950         if(!this.multiple){
18951             return;
18952         }
18953         
18954         this.item = [];
18955         
18956         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18957            c.remove();
18958         });
18959         
18960         this.syncValue();
18961         
18962         this.validate();
18963         
18964         if(this.tickable && !Roo.isTouch){
18965             this.view.refresh();
18966         }
18967     },
18968     
18969     inputEl: function ()
18970     {
18971         if(Roo.isIOS && this.useNativeIOS){
18972             return this.el.select('select.roo-ios-select', true).first();
18973         }
18974         
18975         if(Roo.isTouch && this.mobileTouchView){
18976             return this.el.select('input.form-control',true).first();
18977         }
18978         
18979         if(this.tickable){
18980             return this.searchField;
18981         }
18982         
18983         return this.el.select('input.form-control',true).first();
18984     },
18985     
18986     onTickableFooterButtonClick : function(e, btn, el)
18987     {
18988         e.preventDefault();
18989         
18990         this.lastItem = Roo.apply([], this.item);
18991         
18992         if(btn && btn.name == 'cancel'){
18993             this.tickItems = Roo.apply([], this.item);
18994             this.collapse();
18995             return;
18996         }
18997         
18998         this.clearItem();
18999         
19000         var _this = this;
19001         
19002         Roo.each(this.tickItems, function(o){
19003             _this.addItem(o);
19004         });
19005         
19006         this.collapse();
19007         
19008     },
19009     
19010     validate : function()
19011     {
19012         if(this.getVisibilityEl().hasClass('hidden')){
19013             return true;
19014         }
19015         
19016         var v = this.getRawValue();
19017         
19018         if(this.multiple){
19019             v = this.getValue();
19020         }
19021         
19022         if(this.disabled || this.allowBlank || v.length){
19023             this.markValid();
19024             return true;
19025         }
19026         
19027         this.markInvalid();
19028         return false;
19029     },
19030     
19031     tickableInputEl : function()
19032     {
19033         if(!this.tickable || !this.editable){
19034             return this.inputEl();
19035         }
19036         
19037         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19038     },
19039     
19040     
19041     getAutoCreateTouchView : function()
19042     {
19043         var id = Roo.id();
19044         
19045         var cfg = {
19046             cls: 'form-group' //input-group
19047         };
19048         
19049         var input =  {
19050             tag: 'input',
19051             id : id,
19052             type : this.inputType,
19053             cls : 'form-control x-combo-noedit',
19054             autocomplete: 'new-password',
19055             placeholder : this.placeholder || '',
19056             readonly : true
19057         };
19058         
19059         if (this.name) {
19060             input.name = this.name;
19061         }
19062         
19063         if (this.size) {
19064             input.cls += ' input-' + this.size;
19065         }
19066         
19067         if (this.disabled) {
19068             input.disabled = true;
19069         }
19070         
19071         var inputblock = {
19072             cls : 'roo-combobox-wrap',
19073             cn : [
19074                 input
19075             ]
19076         };
19077         
19078         if(this.before){
19079             inputblock.cls += ' input-group';
19080             
19081             inputblock.cn.unshift({
19082                 tag :'span',
19083                 cls : 'input-group-addon input-group-prepend input-group-text',
19084                 html : this.before
19085             });
19086         }
19087         
19088         if(this.removable && !this.multiple){
19089             inputblock.cls += ' roo-removable';
19090             
19091             inputblock.cn.push({
19092                 tag: 'button',
19093                 html : 'x',
19094                 cls : 'roo-combo-removable-btn close'
19095             });
19096         }
19097
19098         if(this.hasFeedback && !this.allowBlank){
19099             
19100             inputblock.cls += ' has-feedback';
19101             
19102             inputblock.cn.push({
19103                 tag: 'span',
19104                 cls: 'glyphicon form-control-feedback'
19105             });
19106             
19107         }
19108         
19109         if (this.after) {
19110             
19111             inputblock.cls += (this.before) ? '' : ' input-group';
19112             
19113             inputblock.cn.push({
19114                 tag :'span',
19115                 cls : 'input-group-addon input-group-append input-group-text',
19116                 html : this.after
19117             });
19118         }
19119
19120         
19121         var ibwrap = inputblock;
19122         
19123         if(this.multiple){
19124             ibwrap = {
19125                 tag: 'ul',
19126                 cls: 'roo-select2-choices',
19127                 cn:[
19128                     {
19129                         tag: 'li',
19130                         cls: 'roo-select2-search-field',
19131                         cn: [
19132
19133                             inputblock
19134                         ]
19135                     }
19136                 ]
19137             };
19138         
19139             
19140         }
19141         
19142         var combobox = {
19143             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19144             cn: [
19145                 {
19146                     tag: 'input',
19147                     type : 'hidden',
19148                     cls: 'form-hidden-field'
19149                 },
19150                 ibwrap
19151             ]
19152         };
19153         
19154         if(!this.multiple && this.showToggleBtn){
19155             
19156             var caret = {
19157                 cls: 'caret'
19158             };
19159             
19160             if (this.caret != false) {
19161                 caret = {
19162                      tag: 'i',
19163                      cls: 'fa fa-' + this.caret
19164                 };
19165                 
19166             }
19167             
19168             combobox.cn.push({
19169                 tag :'span',
19170                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19171                 cn : [
19172                     Roo.bootstrap.version == 3 ? caret : '',
19173                     {
19174                         tag: 'span',
19175                         cls: 'combobox-clear',
19176                         cn  : [
19177                             {
19178                                 tag : 'i',
19179                                 cls: 'icon-remove'
19180                             }
19181                         ]
19182                     }
19183                 ]
19184
19185             })
19186         }
19187         
19188         if(this.multiple){
19189             combobox.cls += ' roo-select2-container-multi';
19190         }
19191         
19192         var required =  this.allowBlank ?  {
19193                     tag : 'i',
19194                     style: 'display: none'
19195                 } : {
19196                    tag : 'i',
19197                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19198                    tooltip : 'This field is required'
19199                 };
19200         
19201         var align = this.labelAlign || this.parentLabelAlign();
19202         
19203         if (align ==='left' && this.fieldLabel.length) {
19204
19205             cfg.cn = [
19206                 required,
19207                 {
19208                     tag: 'label',
19209                     cls : 'control-label col-form-label',
19210                     html : this.fieldLabel
19211
19212                 },
19213                 {
19214                     cls : 'roo-combobox-wrap ', 
19215                     cn: [
19216                         combobox
19217                     ]
19218                 }
19219             ];
19220             
19221             var labelCfg = cfg.cn[1];
19222             var contentCfg = cfg.cn[2];
19223             
19224
19225             if(this.indicatorpos == 'right'){
19226                 cfg.cn = [
19227                     {
19228                         tag: 'label',
19229                         'for' :  id,
19230                         cls : 'control-label col-form-label',
19231                         cn : [
19232                             {
19233                                 tag : 'span',
19234                                 html : this.fieldLabel
19235                             },
19236                             required
19237                         ]
19238                     },
19239                     {
19240                         cls : "roo-combobox-wrap ",
19241                         cn: [
19242                             combobox
19243                         ]
19244                     }
19245
19246                 ];
19247                 
19248                 labelCfg = cfg.cn[0];
19249                 contentCfg = cfg.cn[1];
19250             }
19251             
19252            
19253             
19254             if(this.labelWidth > 12){
19255                 labelCfg.style = "width: " + this.labelWidth + 'px';
19256             }
19257            
19258             if(this.labelWidth < 13 && this.labelmd == 0){
19259                 this.labelmd = this.labelWidth;
19260             }
19261             
19262             if(this.labellg > 0){
19263                 labelCfg.cls += ' col-lg-' + this.labellg;
19264                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19265             }
19266             
19267             if(this.labelmd > 0){
19268                 labelCfg.cls += ' col-md-' + this.labelmd;
19269                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19270             }
19271             
19272             if(this.labelsm > 0){
19273                 labelCfg.cls += ' col-sm-' + this.labelsm;
19274                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19275             }
19276             
19277             if(this.labelxs > 0){
19278                 labelCfg.cls += ' col-xs-' + this.labelxs;
19279                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19280             }
19281                 
19282                 
19283         } else if ( this.fieldLabel.length) {
19284             cfg.cn = [
19285                required,
19286                 {
19287                     tag: 'label',
19288                     cls : 'control-label',
19289                     html : this.fieldLabel
19290
19291                 },
19292                 {
19293                     cls : '', 
19294                     cn: [
19295                         combobox
19296                     ]
19297                 }
19298             ];
19299             
19300             if(this.indicatorpos == 'right'){
19301                 cfg.cn = [
19302                     {
19303                         tag: 'label',
19304                         cls : 'control-label',
19305                         html : this.fieldLabel,
19306                         cn : [
19307                             required
19308                         ]
19309                     },
19310                     {
19311                         cls : '', 
19312                         cn: [
19313                             combobox
19314                         ]
19315                     }
19316                 ];
19317             }
19318         } else {
19319             cfg.cn = combobox;    
19320         }
19321         
19322         
19323         var settings = this;
19324         
19325         ['xs','sm','md','lg'].map(function(size){
19326             if (settings[size]) {
19327                 cfg.cls += ' col-' + size + '-' + settings[size];
19328             }
19329         });
19330         
19331         return cfg;
19332     },
19333     
19334     initTouchView : function()
19335     {
19336         this.renderTouchView();
19337         
19338         this.touchViewEl.on('scroll', function(){
19339             this.el.dom.scrollTop = 0;
19340         }, this);
19341         
19342         this.originalValue = this.getValue();
19343         
19344         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19345         
19346         this.inputEl().on("click", this.showTouchView, this);
19347         if (this.triggerEl) {
19348             this.triggerEl.on("click", this.showTouchView, this);
19349         }
19350         
19351         
19352         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19353         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19354         
19355         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19356         
19357         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19358         this.store.on('load', this.onTouchViewLoad, this);
19359         this.store.on('loadexception', this.onTouchViewLoadException, this);
19360         
19361         if(this.hiddenName){
19362             
19363             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19364             
19365             this.hiddenField.dom.value =
19366                 this.hiddenValue !== undefined ? this.hiddenValue :
19367                 this.value !== undefined ? this.value : '';
19368         
19369             this.el.dom.removeAttribute('name');
19370             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19371         }
19372         
19373         if(this.multiple){
19374             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19375             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19376         }
19377         
19378         if(this.removable && !this.multiple){
19379             var close = this.closeTriggerEl();
19380             if(close){
19381                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19382                 close.on('click', this.removeBtnClick, this, close);
19383             }
19384         }
19385         /*
19386          * fix the bug in Safari iOS8
19387          */
19388         this.inputEl().on("focus", function(e){
19389             document.activeElement.blur();
19390         }, this);
19391         
19392         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19393         
19394         return;
19395         
19396         
19397     },
19398     
19399     renderTouchView : function()
19400     {
19401         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19402         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19403         
19404         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19405         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19406         
19407         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19408         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19409         this.touchViewBodyEl.setStyle('overflow', 'auto');
19410         
19411         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19412         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19413         
19414         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19415         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19416         
19417     },
19418     
19419     showTouchView : function()
19420     {
19421         if(this.disabled){
19422             return;
19423         }
19424         
19425         this.touchViewHeaderEl.hide();
19426
19427         if(this.modalTitle.length){
19428             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19429             this.touchViewHeaderEl.show();
19430         }
19431
19432         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19433         this.touchViewEl.show();
19434
19435         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19436         
19437         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19438         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19439
19440         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19441
19442         if(this.modalTitle.length){
19443             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19444         }
19445         
19446         this.touchViewBodyEl.setHeight(bodyHeight);
19447
19448         if(this.animate){
19449             var _this = this;
19450             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19451         }else{
19452             this.touchViewEl.addClass(['in','show']);
19453         }
19454         
19455         if(this._touchViewMask){
19456             Roo.get(document.body).addClass("x-body-masked");
19457             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19458             this._touchViewMask.setStyle('z-index', 10000);
19459             this._touchViewMask.addClass('show');
19460         }
19461         
19462         this.doTouchViewQuery();
19463         
19464     },
19465     
19466     hideTouchView : function()
19467     {
19468         this.touchViewEl.removeClass(['in','show']);
19469
19470         if(this.animate){
19471             var _this = this;
19472             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19473         }else{
19474             this.touchViewEl.setStyle('display', 'none');
19475         }
19476         
19477         if(this._touchViewMask){
19478             this._touchViewMask.removeClass('show');
19479             Roo.get(document.body).removeClass("x-body-masked");
19480         }
19481     },
19482     
19483     setTouchViewValue : function()
19484     {
19485         if(this.multiple){
19486             this.clearItem();
19487         
19488             var _this = this;
19489
19490             Roo.each(this.tickItems, function(o){
19491                 this.addItem(o);
19492             }, this);
19493         }
19494         
19495         this.hideTouchView();
19496     },
19497     
19498     doTouchViewQuery : function()
19499     {
19500         var qe = {
19501             query: '',
19502             forceAll: true,
19503             combo: this,
19504             cancel:false
19505         };
19506         
19507         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19508             return false;
19509         }
19510         
19511         if(!this.alwaysQuery || this.mode == 'local'){
19512             this.onTouchViewLoad();
19513             return;
19514         }
19515         
19516         this.store.load();
19517     },
19518     
19519     onTouchViewBeforeLoad : function(combo,opts)
19520     {
19521         return;
19522     },
19523
19524     // private
19525     onTouchViewLoad : function()
19526     {
19527         if(this.store.getCount() < 1){
19528             this.onTouchViewEmptyResults();
19529             return;
19530         }
19531         
19532         this.clearTouchView();
19533         
19534         var rawValue = this.getRawValue();
19535         
19536         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19537         
19538         this.tickItems = [];
19539         
19540         this.store.data.each(function(d, rowIndex){
19541             var row = this.touchViewListGroup.createChild(template);
19542             
19543             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19544                 row.addClass(d.data.cls);
19545             }
19546             
19547             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19548                 var cfg = {
19549                     data : d.data,
19550                     html : d.data[this.displayField]
19551                 };
19552                 
19553                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19554                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19555                 }
19556             }
19557             row.removeClass('selected');
19558             if(!this.multiple && this.valueField &&
19559                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19560             {
19561                 // radio buttons..
19562                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19563                 row.addClass('selected');
19564             }
19565             
19566             if(this.multiple && this.valueField &&
19567                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19568             {
19569                 
19570                 // checkboxes...
19571                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19572                 this.tickItems.push(d.data);
19573             }
19574             
19575             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19576             
19577         }, this);
19578         
19579         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19580         
19581         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19582
19583         if(this.modalTitle.length){
19584             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19585         }
19586
19587         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19588         
19589         if(this.mobile_restrict_height && listHeight < bodyHeight){
19590             this.touchViewBodyEl.setHeight(listHeight);
19591         }
19592         
19593         var _this = this;
19594         
19595         if(firstChecked && listHeight > bodyHeight){
19596             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19597         }
19598         
19599     },
19600     
19601     onTouchViewLoadException : function()
19602     {
19603         this.hideTouchView();
19604     },
19605     
19606     onTouchViewEmptyResults : function()
19607     {
19608         this.clearTouchView();
19609         
19610         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19611         
19612         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19613         
19614     },
19615     
19616     clearTouchView : function()
19617     {
19618         this.touchViewListGroup.dom.innerHTML = '';
19619     },
19620     
19621     onTouchViewClick : function(e, el, o)
19622     {
19623         e.preventDefault();
19624         
19625         var row = o.row;
19626         var rowIndex = o.rowIndex;
19627         
19628         var r = this.store.getAt(rowIndex);
19629         
19630         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19631             
19632             if(!this.multiple){
19633                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19634                     c.dom.removeAttribute('checked');
19635                 }, this);
19636
19637                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19638
19639                 this.setFromData(r.data);
19640
19641                 var close = this.closeTriggerEl();
19642
19643                 if(close){
19644                     close.show();
19645                 }
19646
19647                 this.hideTouchView();
19648
19649                 this.fireEvent('select', this, r, rowIndex);
19650
19651                 return;
19652             }
19653
19654             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19655                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19656                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19657                 return;
19658             }
19659
19660             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19661             this.addItem(r.data);
19662             this.tickItems.push(r.data);
19663         }
19664     },
19665     
19666     getAutoCreateNativeIOS : function()
19667     {
19668         var cfg = {
19669             cls: 'form-group' //input-group,
19670         };
19671         
19672         var combobox =  {
19673             tag: 'select',
19674             cls : 'roo-ios-select'
19675         };
19676         
19677         if (this.name) {
19678             combobox.name = this.name;
19679         }
19680         
19681         if (this.disabled) {
19682             combobox.disabled = true;
19683         }
19684         
19685         var settings = this;
19686         
19687         ['xs','sm','md','lg'].map(function(size){
19688             if (settings[size]) {
19689                 cfg.cls += ' col-' + size + '-' + settings[size];
19690             }
19691         });
19692         
19693         cfg.cn = combobox;
19694         
19695         return cfg;
19696         
19697     },
19698     
19699     initIOSView : function()
19700     {
19701         this.store.on('load', this.onIOSViewLoad, this);
19702         
19703         return;
19704     },
19705     
19706     onIOSViewLoad : function()
19707     {
19708         if(this.store.getCount() < 1){
19709             return;
19710         }
19711         
19712         this.clearIOSView();
19713         
19714         if(this.allowBlank) {
19715             
19716             var default_text = '-- SELECT --';
19717             
19718             if(this.placeholder.length){
19719                 default_text = this.placeholder;
19720             }
19721             
19722             if(this.emptyTitle.length){
19723                 default_text += ' - ' + this.emptyTitle + ' -';
19724             }
19725             
19726             var opt = this.inputEl().createChild({
19727                 tag: 'option',
19728                 value : 0,
19729                 html : default_text
19730             });
19731             
19732             var o = {};
19733             o[this.valueField] = 0;
19734             o[this.displayField] = default_text;
19735             
19736             this.ios_options.push({
19737                 data : o,
19738                 el : opt
19739             });
19740             
19741         }
19742         
19743         this.store.data.each(function(d, rowIndex){
19744             
19745             var html = '';
19746             
19747             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19748                 html = d.data[this.displayField];
19749             }
19750             
19751             var value = '';
19752             
19753             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19754                 value = d.data[this.valueField];
19755             }
19756             
19757             var option = {
19758                 tag: 'option',
19759                 value : value,
19760                 html : html
19761             };
19762             
19763             if(this.value == d.data[this.valueField]){
19764                 option['selected'] = true;
19765             }
19766             
19767             var opt = this.inputEl().createChild(option);
19768             
19769             this.ios_options.push({
19770                 data : d.data,
19771                 el : opt
19772             });
19773             
19774         }, this);
19775         
19776         this.inputEl().on('change', function(){
19777            this.fireEvent('select', this);
19778         }, this);
19779         
19780     },
19781     
19782     clearIOSView: function()
19783     {
19784         this.inputEl().dom.innerHTML = '';
19785         
19786         this.ios_options = [];
19787     },
19788     
19789     setIOSValue: function(v)
19790     {
19791         this.value = v;
19792         
19793         if(!this.ios_options){
19794             return;
19795         }
19796         
19797         Roo.each(this.ios_options, function(opts){
19798            
19799            opts.el.dom.removeAttribute('selected');
19800            
19801            if(opts.data[this.valueField] != v){
19802                return;
19803            }
19804            
19805            opts.el.dom.setAttribute('selected', true);
19806            
19807         }, this);
19808     }
19809
19810     /** 
19811     * @cfg {Boolean} grow 
19812     * @hide 
19813     */
19814     /** 
19815     * @cfg {Number} growMin 
19816     * @hide 
19817     */
19818     /** 
19819     * @cfg {Number} growMax 
19820     * @hide 
19821     */
19822     /**
19823      * @hide
19824      * @method autoSize
19825      */
19826 });
19827
19828 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19829     
19830     header : {
19831         tag: 'div',
19832         cls: 'modal-header',
19833         cn: [
19834             {
19835                 tag: 'h4',
19836                 cls: 'modal-title'
19837             }
19838         ]
19839     },
19840     
19841     body : {
19842         tag: 'div',
19843         cls: 'modal-body',
19844         cn: [
19845             {
19846                 tag: 'ul',
19847                 cls: 'list-group'
19848             }
19849         ]
19850     },
19851     
19852     listItemRadio : {
19853         tag: 'li',
19854         cls: 'list-group-item',
19855         cn: [
19856             {
19857                 tag: 'span',
19858                 cls: 'roo-combobox-list-group-item-value'
19859             },
19860             {
19861                 tag: 'div',
19862                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19863                 cn: [
19864                     {
19865                         tag: 'input',
19866                         type: 'radio'
19867                     },
19868                     {
19869                         tag: 'label'
19870                     }
19871                 ]
19872             }
19873         ]
19874     },
19875     
19876     listItemCheckbox : {
19877         tag: 'li',
19878         cls: 'list-group-item',
19879         cn: [
19880             {
19881                 tag: 'span',
19882                 cls: 'roo-combobox-list-group-item-value'
19883             },
19884             {
19885                 tag: 'div',
19886                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19887                 cn: [
19888                     {
19889                         tag: 'input',
19890                         type: 'checkbox'
19891                     },
19892                     {
19893                         tag: 'label'
19894                     }
19895                 ]
19896             }
19897         ]
19898     },
19899     
19900     emptyResult : {
19901         tag: 'div',
19902         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19903     },
19904     
19905     footer : {
19906         tag: 'div',
19907         cls: 'modal-footer',
19908         cn: [
19909             {
19910                 tag: 'div',
19911                 cls: 'row',
19912                 cn: [
19913                     {
19914                         tag: 'div',
19915                         cls: 'col-xs-6 text-left',
19916                         cn: {
19917                             tag: 'button',
19918                             cls: 'btn btn-danger roo-touch-view-cancel',
19919                             html: 'Cancel'
19920                         }
19921                     },
19922                     {
19923                         tag: 'div',
19924                         cls: 'col-xs-6 text-right',
19925                         cn: {
19926                             tag: 'button',
19927                             cls: 'btn btn-success roo-touch-view-ok',
19928                             html: 'OK'
19929                         }
19930                     }
19931                 ]
19932             }
19933         ]
19934         
19935     }
19936 });
19937
19938 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19939     
19940     touchViewTemplate : {
19941         tag: 'div',
19942         cls: 'modal fade roo-combobox-touch-view',
19943         cn: [
19944             {
19945                 tag: 'div',
19946                 cls: 'modal-dialog',
19947                 style : 'position:fixed', // we have to fix position....
19948                 cn: [
19949                     {
19950                         tag: 'div',
19951                         cls: 'modal-content',
19952                         cn: [
19953                             Roo.bootstrap.form.ComboBox.header,
19954                             Roo.bootstrap.form.ComboBox.body,
19955                             Roo.bootstrap.form.ComboBox.footer
19956                         ]
19957                     }
19958                 ]
19959             }
19960         ]
19961     }
19962 });/*
19963  * Based on:
19964  * Ext JS Library 1.1.1
19965  * Copyright(c) 2006-2007, Ext JS, LLC.
19966  *
19967  * Originally Released Under LGPL - original licence link has changed is not relivant.
19968  *
19969  * Fork - LGPL
19970  * <script type="text/javascript">
19971  */
19972
19973 /**
19974  * @class Roo.View
19975  * @extends Roo.util.Observable
19976  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19977  * This class also supports single and multi selection modes. <br>
19978  * Create a data model bound view:
19979  <pre><code>
19980  var store = new Roo.data.Store(...);
19981
19982  var view = new Roo.View({
19983     el : "my-element",
19984     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19985  
19986     singleSelect: true,
19987     selectedClass: "ydataview-selected",
19988     store: store
19989  });
19990
19991  // listen for node click?
19992  view.on("click", function(vw, index, node, e){
19993  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19994  });
19995
19996  // load XML data
19997  dataModel.load("foobar.xml");
19998  </code></pre>
19999  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
20000  * <br><br>
20001  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
20002  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
20003  * 
20004  * Note: old style constructor is still suported (container, template, config)
20005  * 
20006  * @constructor
20007  * Create a new View
20008  * @param {Object} config The config object
20009  * 
20010  */
20011 Roo.View = function(config, depreciated_tpl, depreciated_config){
20012     
20013     this.parent = false;
20014     
20015     if (typeof(depreciated_tpl) == 'undefined') {
20016         // new way.. - universal constructor.
20017         Roo.apply(this, config);
20018         this.el  = Roo.get(this.el);
20019     } else {
20020         // old format..
20021         this.el  = Roo.get(config);
20022         this.tpl = depreciated_tpl;
20023         Roo.apply(this, depreciated_config);
20024     }
20025     this.wrapEl  = this.el.wrap().wrap();
20026     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
20027     
20028     
20029     if(typeof(this.tpl) == "string"){
20030         this.tpl = new Roo.Template(this.tpl);
20031     } else {
20032         // support xtype ctors..
20033         this.tpl = new Roo.factory(this.tpl, Roo);
20034     }
20035     
20036     
20037     this.tpl.compile();
20038     
20039     /** @private */
20040     this.addEvents({
20041         /**
20042          * @event beforeclick
20043          * Fires before a click is processed. Returns false to cancel the default action.
20044          * @param {Roo.View} this
20045          * @param {Number} index The index of the target node
20046          * @param {HTMLElement} node The target node
20047          * @param {Roo.EventObject} e The raw event object
20048          */
20049             "beforeclick" : true,
20050         /**
20051          * @event click
20052          * Fires when a template node is clicked.
20053          * @param {Roo.View} this
20054          * @param {Number} index The index of the target node
20055          * @param {HTMLElement} node The target node
20056          * @param {Roo.EventObject} e The raw event object
20057          */
20058             "click" : true,
20059         /**
20060          * @event dblclick
20061          * Fires when a template node is double clicked.
20062          * @param {Roo.View} this
20063          * @param {Number} index The index of the target node
20064          * @param {HTMLElement} node The target node
20065          * @param {Roo.EventObject} e The raw event object
20066          */
20067             "dblclick" : true,
20068         /**
20069          * @event contextmenu
20070          * Fires when a template node is right clicked.
20071          * @param {Roo.View} this
20072          * @param {Number} index The index of the target node
20073          * @param {HTMLElement} node The target node
20074          * @param {Roo.EventObject} e The raw event object
20075          */
20076             "contextmenu" : true,
20077         /**
20078          * @event selectionchange
20079          * Fires when the selected nodes change.
20080          * @param {Roo.View} this
20081          * @param {Array} selections Array of the selected nodes
20082          */
20083             "selectionchange" : true,
20084     
20085         /**
20086          * @event beforeselect
20087          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20088          * @param {Roo.View} this
20089          * @param {HTMLElement} node The node to be selected
20090          * @param {Array} selections Array of currently selected nodes
20091          */
20092             "beforeselect" : true,
20093         /**
20094          * @event preparedata
20095          * Fires on every row to render, to allow you to change the data.
20096          * @param {Roo.View} this
20097          * @param {Object} data to be rendered (change this)
20098          */
20099           "preparedata" : true
20100           
20101           
20102         });
20103
20104
20105
20106     this.el.on({
20107         "click": this.onClick,
20108         "dblclick": this.onDblClick,
20109         "contextmenu": this.onContextMenu,
20110         scope:this
20111     });
20112
20113     this.selections = [];
20114     this.nodes = [];
20115     this.cmp = new Roo.CompositeElementLite([]);
20116     if(this.store){
20117         this.store = Roo.factory(this.store, Roo.data);
20118         this.setStore(this.store, true);
20119     }
20120     
20121     if ( this.footer && this.footer.xtype) {
20122            
20123          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20124         
20125         this.footer.dataSource = this.store;
20126         this.footer.container = fctr;
20127         this.footer = Roo.factory(this.footer, Roo);
20128         fctr.insertFirst(this.el);
20129         
20130         // this is a bit insane - as the paging toolbar seems to detach the el..
20131 //        dom.parentNode.parentNode.parentNode
20132          // they get detached?
20133     }
20134     
20135     
20136     Roo.View.superclass.constructor.call(this);
20137     
20138     
20139 };
20140
20141 Roo.extend(Roo.View, Roo.util.Observable, {
20142     
20143      /**
20144      * @cfg {Roo.data.Store} store Data store to load data from.
20145      */
20146     store : false,
20147     
20148     /**
20149      * @cfg {String|Roo.Element} el The container element.
20150      */
20151     el : '',
20152     
20153     /**
20154      * @cfg {String|Roo.Template} tpl The template used by this View 
20155      */
20156     tpl : false,
20157     /**
20158      * @cfg {String} dataName the named area of the template to use as the data area
20159      *                          Works with domtemplates roo-name="name"
20160      */
20161     dataName: false,
20162     /**
20163      * @cfg {String} selectedClass The css class to add to selected nodes
20164      */
20165     selectedClass : "x-view-selected",
20166      /**
20167      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20168      */
20169     emptyText : "",
20170     
20171     /**
20172      * @cfg {String} text to display on mask (default Loading)
20173      */
20174     mask : false,
20175     /**
20176      * @cfg {Boolean} multiSelect Allow multiple selection
20177      */
20178     multiSelect : false,
20179     /**
20180      * @cfg {Boolean} singleSelect Allow single selection
20181      */
20182     singleSelect:  false,
20183     
20184     /**
20185      * @cfg {Boolean} toggleSelect - selecting 
20186      */
20187     toggleSelect : false,
20188     
20189     /**
20190      * @cfg {Boolean} tickable - selecting 
20191      */
20192     tickable : false,
20193     
20194     /**
20195      * Returns the element this view is bound to.
20196      * @return {Roo.Element}
20197      */
20198     getEl : function(){
20199         return this.wrapEl;
20200     },
20201     
20202     
20203
20204     /**
20205      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20206      */
20207     refresh : function(){
20208         //Roo.log('refresh');
20209         var t = this.tpl;
20210         
20211         // if we are using something like 'domtemplate', then
20212         // the what gets used is:
20213         // t.applySubtemplate(NAME, data, wrapping data..)
20214         // the outer template then get' applied with
20215         //     the store 'extra data'
20216         // and the body get's added to the
20217         //      roo-name="data" node?
20218         //      <span class='roo-tpl-{name}'></span> ?????
20219         
20220         
20221         
20222         this.clearSelections();
20223         this.el.update("");
20224         var html = [];
20225         var records = this.store.getRange();
20226         if(records.length < 1) {
20227             
20228             // is this valid??  = should it render a template??
20229             
20230             this.el.update(this.emptyText);
20231             return;
20232         }
20233         var el = this.el;
20234         if (this.dataName) {
20235             this.el.update(t.apply(this.store.meta)); //????
20236             el = this.el.child('.roo-tpl-' + this.dataName);
20237         }
20238         
20239         for(var i = 0, len = records.length; i < len; i++){
20240             var data = this.prepareData(records[i].data, i, records[i]);
20241             this.fireEvent("preparedata", this, data, i, records[i]);
20242             
20243             var d = Roo.apply({}, data);
20244             
20245             if(this.tickable){
20246                 Roo.apply(d, {'roo-id' : Roo.id()});
20247                 
20248                 var _this = this;
20249             
20250                 Roo.each(this.parent.item, function(item){
20251                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20252                         return;
20253                     }
20254                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20255                 });
20256             }
20257             
20258             html[html.length] = Roo.util.Format.trim(
20259                 this.dataName ?
20260                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20261                     t.apply(d)
20262             );
20263         }
20264         
20265         
20266         
20267         el.update(html.join(""));
20268         this.nodes = el.dom.childNodes;
20269         this.updateIndexes(0);
20270     },
20271     
20272
20273     /**
20274      * Function to override to reformat the data that is sent to
20275      * the template for each node.
20276      * DEPRICATED - use the preparedata event handler.
20277      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20278      * a JSON object for an UpdateManager bound view).
20279      */
20280     prepareData : function(data, index, record)
20281     {
20282         this.fireEvent("preparedata", this, data, index, record);
20283         return data;
20284     },
20285
20286     onUpdate : function(ds, record){
20287         // Roo.log('on update');   
20288         this.clearSelections();
20289         var index = this.store.indexOf(record);
20290         var n = this.nodes[index];
20291         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20292         n.parentNode.removeChild(n);
20293         this.updateIndexes(index, index);
20294     },
20295
20296     
20297     
20298 // --------- FIXME     
20299     onAdd : function(ds, records, index)
20300     {
20301         //Roo.log(['on Add', ds, records, index] );        
20302         this.clearSelections();
20303         if(this.nodes.length == 0){
20304             this.refresh();
20305             return;
20306         }
20307         var n = this.nodes[index];
20308         for(var i = 0, len = records.length; i < len; i++){
20309             var d = this.prepareData(records[i].data, i, records[i]);
20310             if(n){
20311                 this.tpl.insertBefore(n, d);
20312             }else{
20313                 
20314                 this.tpl.append(this.el, d);
20315             }
20316         }
20317         this.updateIndexes(index);
20318     },
20319
20320     onRemove : function(ds, record, index){
20321        // Roo.log('onRemove');
20322         this.clearSelections();
20323         var el = this.dataName  ?
20324             this.el.child('.roo-tpl-' + this.dataName) :
20325             this.el; 
20326         
20327         el.dom.removeChild(this.nodes[index]);
20328         this.updateIndexes(index);
20329     },
20330
20331     /**
20332      * Refresh an individual node.
20333      * @param {Number} index
20334      */
20335     refreshNode : function(index){
20336         this.onUpdate(this.store, this.store.getAt(index));
20337     },
20338
20339     updateIndexes : function(startIndex, endIndex){
20340         var ns = this.nodes;
20341         startIndex = startIndex || 0;
20342         endIndex = endIndex || ns.length - 1;
20343         for(var i = startIndex; i <= endIndex; i++){
20344             ns[i].nodeIndex = i;
20345         }
20346     },
20347
20348     /**
20349      * Changes the data store this view uses and refresh the view.
20350      * @param {Store} store
20351      */
20352     setStore : function(store, initial){
20353         if(!initial && this.store){
20354             this.store.un("datachanged", this.refresh);
20355             this.store.un("add", this.onAdd);
20356             this.store.un("remove", this.onRemove);
20357             this.store.un("update", this.onUpdate);
20358             this.store.un("clear", this.refresh);
20359             this.store.un("beforeload", this.onBeforeLoad);
20360             this.store.un("load", this.onLoad);
20361             this.store.un("loadexception", this.onLoad);
20362         }
20363         if(store){
20364           
20365             store.on("datachanged", this.refresh, this);
20366             store.on("add", this.onAdd, this);
20367             store.on("remove", this.onRemove, this);
20368             store.on("update", this.onUpdate, this);
20369             store.on("clear", this.refresh, this);
20370             store.on("beforeload", this.onBeforeLoad, this);
20371             store.on("load", this.onLoad, this);
20372             store.on("loadexception", this.onLoad, this);
20373         }
20374         
20375         if(store){
20376             this.refresh();
20377         }
20378     },
20379     /**
20380      * onbeforeLoad - masks the loading area.
20381      *
20382      */
20383     onBeforeLoad : function(store,opts)
20384     {
20385          //Roo.log('onBeforeLoad');   
20386         if (!opts.add) {
20387             this.el.update("");
20388         }
20389         this.el.mask(this.mask ? this.mask : "Loading" ); 
20390     },
20391     onLoad : function ()
20392     {
20393         this.el.unmask();
20394     },
20395     
20396
20397     /**
20398      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20399      * @param {HTMLElement} node
20400      * @return {HTMLElement} The template node
20401      */
20402     findItemFromChild : function(node){
20403         var el = this.dataName  ?
20404             this.el.child('.roo-tpl-' + this.dataName,true) :
20405             this.el.dom; 
20406         
20407         if(!node || node.parentNode == el){
20408                     return node;
20409             }
20410             var p = node.parentNode;
20411             while(p && p != el){
20412             if(p.parentNode == el){
20413                 return p;
20414             }
20415             p = p.parentNode;
20416         }
20417             return null;
20418     },
20419
20420     /** @ignore */
20421     onClick : function(e){
20422         var item = this.findItemFromChild(e.getTarget());
20423         if(item){
20424             var index = this.indexOf(item);
20425             if(this.onItemClick(item, index, e) !== false){
20426                 this.fireEvent("click", this, index, item, e);
20427             }
20428         }else{
20429             this.clearSelections();
20430         }
20431     },
20432
20433     /** @ignore */
20434     onContextMenu : function(e){
20435         var item = this.findItemFromChild(e.getTarget());
20436         if(item){
20437             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20438         }
20439     },
20440
20441     /** @ignore */
20442     onDblClick : function(e){
20443         var item = this.findItemFromChild(e.getTarget());
20444         if(item){
20445             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20446         }
20447     },
20448
20449     onItemClick : function(item, index, e)
20450     {
20451         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20452             return false;
20453         }
20454         if (this.toggleSelect) {
20455             var m = this.isSelected(item) ? 'unselect' : 'select';
20456             //Roo.log(m);
20457             var _t = this;
20458             _t[m](item, true, false);
20459             return true;
20460         }
20461         if(this.multiSelect || this.singleSelect){
20462             if(this.multiSelect && e.shiftKey && this.lastSelection){
20463                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20464             }else{
20465                 this.select(item, this.multiSelect && e.ctrlKey);
20466                 this.lastSelection = item;
20467             }
20468             
20469             if(!this.tickable){
20470                 e.preventDefault();
20471             }
20472             
20473         }
20474         return true;
20475     },
20476
20477     /**
20478      * Get the number of selected nodes.
20479      * @return {Number}
20480      */
20481     getSelectionCount : function(){
20482         return this.selections.length;
20483     },
20484
20485     /**
20486      * Get the currently selected nodes.
20487      * @return {Array} An array of HTMLElements
20488      */
20489     getSelectedNodes : function(){
20490         return this.selections;
20491     },
20492
20493     /**
20494      * Get the indexes of the selected nodes.
20495      * @return {Array}
20496      */
20497     getSelectedIndexes : function(){
20498         var indexes = [], s = this.selections;
20499         for(var i = 0, len = s.length; i < len; i++){
20500             indexes.push(s[i].nodeIndex);
20501         }
20502         return indexes;
20503     },
20504
20505     /**
20506      * Clear all selections
20507      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20508      */
20509     clearSelections : function(suppressEvent){
20510         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20511             this.cmp.elements = this.selections;
20512             this.cmp.removeClass(this.selectedClass);
20513             this.selections = [];
20514             if(!suppressEvent){
20515                 this.fireEvent("selectionchange", this, this.selections);
20516             }
20517         }
20518     },
20519
20520     /**
20521      * Returns true if the passed node is selected
20522      * @param {HTMLElement/Number} node The node or node index
20523      * @return {Boolean}
20524      */
20525     isSelected : function(node){
20526         var s = this.selections;
20527         if(s.length < 1){
20528             return false;
20529         }
20530         node = this.getNode(node);
20531         return s.indexOf(node) !== -1;
20532     },
20533
20534     /**
20535      * Selects nodes.
20536      * @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
20537      * @param {Boolean} keepExisting (optional) true to keep existing selections
20538      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20539      */
20540     select : function(nodeInfo, keepExisting, suppressEvent){
20541         if(nodeInfo instanceof Array){
20542             if(!keepExisting){
20543                 this.clearSelections(true);
20544             }
20545             for(var i = 0, len = nodeInfo.length; i < len; i++){
20546                 this.select(nodeInfo[i], true, true);
20547             }
20548             return;
20549         } 
20550         var node = this.getNode(nodeInfo);
20551         if(!node || this.isSelected(node)){
20552             return; // already selected.
20553         }
20554         if(!keepExisting){
20555             this.clearSelections(true);
20556         }
20557         
20558         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20559             Roo.fly(node).addClass(this.selectedClass);
20560             this.selections.push(node);
20561             if(!suppressEvent){
20562                 this.fireEvent("selectionchange", this, this.selections);
20563             }
20564         }
20565         
20566         
20567     },
20568       /**
20569      * Unselects nodes.
20570      * @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
20571      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20572      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20573      */
20574     unselect : function(nodeInfo, keepExisting, suppressEvent)
20575     {
20576         if(nodeInfo instanceof Array){
20577             Roo.each(this.selections, function(s) {
20578                 this.unselect(s, nodeInfo);
20579             }, this);
20580             return;
20581         }
20582         var node = this.getNode(nodeInfo);
20583         if(!node || !this.isSelected(node)){
20584             //Roo.log("not selected");
20585             return; // not selected.
20586         }
20587         // fireevent???
20588         var ns = [];
20589         Roo.each(this.selections, function(s) {
20590             if (s == node ) {
20591                 Roo.fly(node).removeClass(this.selectedClass);
20592
20593                 return;
20594             }
20595             ns.push(s);
20596         },this);
20597         
20598         this.selections= ns;
20599         this.fireEvent("selectionchange", this, this.selections);
20600     },
20601
20602     /**
20603      * Gets a template node.
20604      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20605      * @return {HTMLElement} The node or null if it wasn't found
20606      */
20607     getNode : function(nodeInfo){
20608         if(typeof nodeInfo == "string"){
20609             return document.getElementById(nodeInfo);
20610         }else if(typeof nodeInfo == "number"){
20611             return this.nodes[nodeInfo];
20612         }
20613         return nodeInfo;
20614     },
20615
20616     /**
20617      * Gets a range template nodes.
20618      * @param {Number} startIndex
20619      * @param {Number} endIndex
20620      * @return {Array} An array of nodes
20621      */
20622     getNodes : function(start, end){
20623         var ns = this.nodes;
20624         start = start || 0;
20625         end = typeof end == "undefined" ? ns.length - 1 : end;
20626         var nodes = [];
20627         if(start <= end){
20628             for(var i = start; i <= end; i++){
20629                 nodes.push(ns[i]);
20630             }
20631         } else{
20632             for(var i = start; i >= end; i--){
20633                 nodes.push(ns[i]);
20634             }
20635         }
20636         return nodes;
20637     },
20638
20639     /**
20640      * Finds the index of the passed node
20641      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20642      * @return {Number} The index of the node or -1
20643      */
20644     indexOf : function(node){
20645         node = this.getNode(node);
20646         if(typeof node.nodeIndex == "number"){
20647             return node.nodeIndex;
20648         }
20649         var ns = this.nodes;
20650         for(var i = 0, len = ns.length; i < len; i++){
20651             if(ns[i] == node){
20652                 return i;
20653             }
20654         }
20655         return -1;
20656     }
20657 });
20658 /*
20659  * - LGPL
20660  *
20661  * based on jquery fullcalendar
20662  * 
20663  */
20664
20665 Roo.bootstrap = Roo.bootstrap || {};
20666 /**
20667  * @class Roo.bootstrap.Calendar
20668  * @extends Roo.bootstrap.Component
20669  * Bootstrap Calendar class
20670  * @cfg {Boolean} loadMask (true|false) default false
20671  * @cfg {Object} header generate the user specific header of the calendar, default false
20672
20673  * @constructor
20674  * Create a new Container
20675  * @param {Object} config The config object
20676  */
20677
20678
20679
20680 Roo.bootstrap.Calendar = function(config){
20681     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20682      this.addEvents({
20683         /**
20684              * @event select
20685              * Fires when a date is selected
20686              * @param {DatePicker} this
20687              * @param {Date} date The selected date
20688              */
20689         'select': true,
20690         /**
20691              * @event monthchange
20692              * Fires when the displayed month changes 
20693              * @param {DatePicker} this
20694              * @param {Date} date The selected month
20695              */
20696         'monthchange': true,
20697         /**
20698              * @event evententer
20699              * Fires when mouse over an event
20700              * @param {Calendar} this
20701              * @param {event} Event
20702              */
20703         'evententer': true,
20704         /**
20705              * @event eventleave
20706              * Fires when the mouse leaves an
20707              * @param {Calendar} this
20708              * @param {event}
20709              */
20710         'eventleave': true,
20711         /**
20712              * @event eventclick
20713              * Fires when the mouse click an
20714              * @param {Calendar} this
20715              * @param {event}
20716              */
20717         'eventclick': true
20718         
20719     });
20720
20721 };
20722
20723 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20724     
20725           /**
20726      * @cfg {Roo.data.Store} store
20727      * The data source for the calendar
20728      */
20729         store : false,
20730      /**
20731      * @cfg {Number} startDay
20732      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20733      */
20734     startDay : 0,
20735     
20736     loadMask : false,
20737     
20738     header : false,
20739       
20740     getAutoCreate : function(){
20741         
20742         
20743         var fc_button = function(name, corner, style, content ) {
20744             return Roo.apply({},{
20745                 tag : 'span',
20746                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20747                          (corner.length ?
20748                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20749                             ''
20750                         ),
20751                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20752                 unselectable: 'on'
20753             });
20754         };
20755         
20756         var header = {};
20757         
20758         if(!this.header){
20759             header = {
20760                 tag : 'table',
20761                 cls : 'fc-header',
20762                 style : 'width:100%',
20763                 cn : [
20764                     {
20765                         tag: 'tr',
20766                         cn : [
20767                             {
20768                                 tag : 'td',
20769                                 cls : 'fc-header-left',
20770                                 cn : [
20771                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20772                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20773                                     { tag: 'span', cls: 'fc-header-space' },
20774                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20775
20776
20777                                 ]
20778                             },
20779
20780                             {
20781                                 tag : 'td',
20782                                 cls : 'fc-header-center',
20783                                 cn : [
20784                                     {
20785                                         tag: 'span',
20786                                         cls: 'fc-header-title',
20787                                         cn : {
20788                                             tag: 'H2',
20789                                             html : 'month / year'
20790                                         }
20791                                     }
20792
20793                                 ]
20794                             },
20795                             {
20796                                 tag : 'td',
20797                                 cls : 'fc-header-right',
20798                                 cn : [
20799                               /*      fc_button('month', 'left', '', 'month' ),
20800                                     fc_button('week', '', '', 'week' ),
20801                                     fc_button('day', 'right', '', 'day' )
20802                                 */    
20803
20804                                 ]
20805                             }
20806
20807                         ]
20808                     }
20809                 ]
20810             };
20811         }
20812         
20813         header = this.header;
20814         
20815        
20816         var cal_heads = function() {
20817             var ret = [];
20818             // fixme - handle this.
20819             
20820             for (var i =0; i < Date.dayNames.length; i++) {
20821                 var d = Date.dayNames[i];
20822                 ret.push({
20823                     tag: 'th',
20824                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20825                     html : d.substring(0,3)
20826                 });
20827                 
20828             }
20829             ret[0].cls += ' fc-first';
20830             ret[6].cls += ' fc-last';
20831             return ret;
20832         };
20833         var cal_cell = function(n) {
20834             return  {
20835                 tag: 'td',
20836                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20837                 cn : [
20838                     {
20839                         cn : [
20840                             {
20841                                 cls: 'fc-day-number',
20842                                 html: 'D'
20843                             },
20844                             {
20845                                 cls: 'fc-day-content',
20846                              
20847                                 cn : [
20848                                      {
20849                                         style: 'position: relative;' // height: 17px;
20850                                     }
20851                                 ]
20852                             }
20853                             
20854                             
20855                         ]
20856                     }
20857                 ]
20858                 
20859             }
20860         };
20861         var cal_rows = function() {
20862             
20863             var ret = [];
20864             for (var r = 0; r < 6; r++) {
20865                 var row= {
20866                     tag : 'tr',
20867                     cls : 'fc-week',
20868                     cn : []
20869                 };
20870                 
20871                 for (var i =0; i < Date.dayNames.length; i++) {
20872                     var d = Date.dayNames[i];
20873                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20874
20875                 }
20876                 row.cn[0].cls+=' fc-first';
20877                 row.cn[0].cn[0].style = 'min-height:90px';
20878                 row.cn[6].cls+=' fc-last';
20879                 ret.push(row);
20880                 
20881             }
20882             ret[0].cls += ' fc-first';
20883             ret[4].cls += ' fc-prev-last';
20884             ret[5].cls += ' fc-last';
20885             return ret;
20886             
20887         };
20888         
20889         var cal_table = {
20890             tag: 'table',
20891             cls: 'fc-border-separate',
20892             style : 'width:100%',
20893             cellspacing  : 0,
20894             cn : [
20895                 { 
20896                     tag: 'thead',
20897                     cn : [
20898                         { 
20899                             tag: 'tr',
20900                             cls : 'fc-first fc-last',
20901                             cn : cal_heads()
20902                         }
20903                     ]
20904                 },
20905                 { 
20906                     tag: 'tbody',
20907                     cn : cal_rows()
20908                 }
20909                   
20910             ]
20911         };
20912          
20913          var cfg = {
20914             cls : 'fc fc-ltr',
20915             cn : [
20916                 header,
20917                 {
20918                     cls : 'fc-content',
20919                     style : "position: relative;",
20920                     cn : [
20921                         {
20922                             cls : 'fc-view fc-view-month fc-grid',
20923                             style : 'position: relative',
20924                             unselectable : 'on',
20925                             cn : [
20926                                 {
20927                                     cls : 'fc-event-container',
20928                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20929                                 },
20930                                 cal_table
20931                             ]
20932                         }
20933                     ]
20934     
20935                 }
20936            ] 
20937             
20938         };
20939         
20940          
20941         
20942         return cfg;
20943     },
20944     
20945     
20946     initEvents : function()
20947     {
20948         if(!this.store){
20949             throw "can not find store for calendar";
20950         }
20951         
20952         var mark = {
20953             tag: "div",
20954             cls:"x-dlg-mask",
20955             style: "text-align:center",
20956             cn: [
20957                 {
20958                     tag: "div",
20959                     style: "background-color:white;width:50%;margin:250 auto",
20960                     cn: [
20961                         {
20962                             tag: "img",
20963                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20964                         },
20965                         {
20966                             tag: "span",
20967                             html: "Loading"
20968                         }
20969                         
20970                     ]
20971                 }
20972             ]
20973         };
20974         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20975         
20976         var size = this.el.select('.fc-content', true).first().getSize();
20977         this.maskEl.setSize(size.width, size.height);
20978         this.maskEl.enableDisplayMode("block");
20979         if(!this.loadMask){
20980             this.maskEl.hide();
20981         }
20982         
20983         this.store = Roo.factory(this.store, Roo.data);
20984         this.store.on('load', this.onLoad, this);
20985         this.store.on('beforeload', this.onBeforeLoad, this);
20986         
20987         this.resize();
20988         
20989         this.cells = this.el.select('.fc-day',true);
20990         //Roo.log(this.cells);
20991         this.textNodes = this.el.query('.fc-day-number');
20992         this.cells.addClassOnOver('fc-state-hover');
20993         
20994         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20995         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20996         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20997         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20998         
20999         this.on('monthchange', this.onMonthChange, this);
21000         
21001         this.update(new Date().clearTime());
21002     },
21003     
21004     resize : function() {
21005         var sz  = this.el.getSize();
21006         
21007         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
21008         this.el.select('.fc-day-content div',true).setHeight(34);
21009     },
21010     
21011     
21012     // private
21013     showPrevMonth : function(e){
21014         this.update(this.activeDate.add("mo", -1));
21015     },
21016     showToday : function(e){
21017         this.update(new Date().clearTime());
21018     },
21019     // private
21020     showNextMonth : function(e){
21021         this.update(this.activeDate.add("mo", 1));
21022     },
21023
21024     // private
21025     showPrevYear : function(){
21026         this.update(this.activeDate.add("y", -1));
21027     },
21028
21029     // private
21030     showNextYear : function(){
21031         this.update(this.activeDate.add("y", 1));
21032     },
21033
21034     
21035    // private
21036     update : function(date)
21037     {
21038         var vd = this.activeDate;
21039         this.activeDate = date;
21040 //        if(vd && this.el){
21041 //            var t = date.getTime();
21042 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21043 //                Roo.log('using add remove');
21044 //                
21045 //                this.fireEvent('monthchange', this, date);
21046 //                
21047 //                this.cells.removeClass("fc-state-highlight");
21048 //                this.cells.each(function(c){
21049 //                   if(c.dateValue == t){
21050 //                       c.addClass("fc-state-highlight");
21051 //                       setTimeout(function(){
21052 //                            try{c.dom.firstChild.focus();}catch(e){}
21053 //                       }, 50);
21054 //                       return false;
21055 //                   }
21056 //                   return true;
21057 //                });
21058 //                return;
21059 //            }
21060 //        }
21061         
21062         var days = date.getDaysInMonth();
21063         
21064         var firstOfMonth = date.getFirstDateOfMonth();
21065         var startingPos = firstOfMonth.getDay()-this.startDay;
21066         
21067         if(startingPos < this.startDay){
21068             startingPos += 7;
21069         }
21070         
21071         var pm = date.add(Date.MONTH, -1);
21072         var prevStart = pm.getDaysInMonth()-startingPos;
21073 //        
21074         this.cells = this.el.select('.fc-day',true);
21075         this.textNodes = this.el.query('.fc-day-number');
21076         this.cells.addClassOnOver('fc-state-hover');
21077         
21078         var cells = this.cells.elements;
21079         var textEls = this.textNodes;
21080         
21081         Roo.each(cells, function(cell){
21082             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21083         });
21084         
21085         days += startingPos;
21086
21087         // convert everything to numbers so it's fast
21088         var day = 86400000;
21089         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21090         //Roo.log(d);
21091         //Roo.log(pm);
21092         //Roo.log(prevStart);
21093         
21094         var today = new Date().clearTime().getTime();
21095         var sel = date.clearTime().getTime();
21096         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21097         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21098         var ddMatch = this.disabledDatesRE;
21099         var ddText = this.disabledDatesText;
21100         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21101         var ddaysText = this.disabledDaysText;
21102         var format = this.format;
21103         
21104         var setCellClass = function(cal, cell){
21105             cell.row = 0;
21106             cell.events = [];
21107             cell.more = [];
21108             //Roo.log('set Cell Class');
21109             cell.title = "";
21110             var t = d.getTime();
21111             
21112             //Roo.log(d);
21113             
21114             cell.dateValue = t;
21115             if(t == today){
21116                 cell.className += " fc-today";
21117                 cell.className += " fc-state-highlight";
21118                 cell.title = cal.todayText;
21119             }
21120             if(t == sel){
21121                 // disable highlight in other month..
21122                 //cell.className += " fc-state-highlight";
21123                 
21124             }
21125             // disabling
21126             if(t < min) {
21127                 cell.className = " fc-state-disabled";
21128                 cell.title = cal.minText;
21129                 return;
21130             }
21131             if(t > max) {
21132                 cell.className = " fc-state-disabled";
21133                 cell.title = cal.maxText;
21134                 return;
21135             }
21136             if(ddays){
21137                 if(ddays.indexOf(d.getDay()) != -1){
21138                     cell.title = ddaysText;
21139                     cell.className = " fc-state-disabled";
21140                 }
21141             }
21142             if(ddMatch && format){
21143                 var fvalue = d.dateFormat(format);
21144                 if(ddMatch.test(fvalue)){
21145                     cell.title = ddText.replace("%0", fvalue);
21146                     cell.className = " fc-state-disabled";
21147                 }
21148             }
21149             
21150             if (!cell.initialClassName) {
21151                 cell.initialClassName = cell.dom.className;
21152             }
21153             
21154             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21155         };
21156
21157         var i = 0;
21158         
21159         for(; i < startingPos; i++) {
21160             textEls[i].innerHTML = (++prevStart);
21161             d.setDate(d.getDate()+1);
21162             
21163             cells[i].className = "fc-past fc-other-month";
21164             setCellClass(this, cells[i]);
21165         }
21166         
21167         var intDay = 0;
21168         
21169         for(; i < days; i++){
21170             intDay = i - startingPos + 1;
21171             textEls[i].innerHTML = (intDay);
21172             d.setDate(d.getDate()+1);
21173             
21174             cells[i].className = ''; // "x-date-active";
21175             setCellClass(this, cells[i]);
21176         }
21177         var extraDays = 0;
21178         
21179         for(; i < 42; i++) {
21180             textEls[i].innerHTML = (++extraDays);
21181             d.setDate(d.getDate()+1);
21182             
21183             cells[i].className = "fc-future fc-other-month";
21184             setCellClass(this, cells[i]);
21185         }
21186         
21187         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21188         
21189         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21190         
21191         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21192         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21193         
21194         if(totalRows != 6){
21195             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21196             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21197         }
21198         
21199         this.fireEvent('monthchange', this, date);
21200         
21201         
21202         /*
21203         if(!this.internalRender){
21204             var main = this.el.dom.firstChild;
21205             var w = main.offsetWidth;
21206             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21207             Roo.fly(main).setWidth(w);
21208             this.internalRender = true;
21209             // opera does not respect the auto grow header center column
21210             // then, after it gets a width opera refuses to recalculate
21211             // without a second pass
21212             if(Roo.isOpera && !this.secondPass){
21213                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21214                 this.secondPass = true;
21215                 this.update.defer(10, this, [date]);
21216             }
21217         }
21218         */
21219         
21220     },
21221     
21222     findCell : function(dt) {
21223         dt = dt.clearTime().getTime();
21224         var ret = false;
21225         this.cells.each(function(c){
21226             //Roo.log("check " +c.dateValue + '?=' + dt);
21227             if(c.dateValue == dt){
21228                 ret = c;
21229                 return false;
21230             }
21231             return true;
21232         });
21233         
21234         return ret;
21235     },
21236     
21237     findCells : function(ev) {
21238         var s = ev.start.clone().clearTime().getTime();
21239        // Roo.log(s);
21240         var e= ev.end.clone().clearTime().getTime();
21241        // Roo.log(e);
21242         var ret = [];
21243         this.cells.each(function(c){
21244              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21245             
21246             if(c.dateValue > e){
21247                 return ;
21248             }
21249             if(c.dateValue < s){
21250                 return ;
21251             }
21252             ret.push(c);
21253         });
21254         
21255         return ret;    
21256     },
21257     
21258 //    findBestRow: function(cells)
21259 //    {
21260 //        var ret = 0;
21261 //        
21262 //        for (var i =0 ; i < cells.length;i++) {
21263 //            ret  = Math.max(cells[i].rows || 0,ret);
21264 //        }
21265 //        return ret;
21266 //        
21267 //    },
21268     
21269     
21270     addItem : function(ev)
21271     {
21272         // look for vertical location slot in
21273         var cells = this.findCells(ev);
21274         
21275 //        ev.row = this.findBestRow(cells);
21276         
21277         // work out the location.
21278         
21279         var crow = false;
21280         var rows = [];
21281         for(var i =0; i < cells.length; i++) {
21282             
21283             cells[i].row = cells[0].row;
21284             
21285             if(i == 0){
21286                 cells[i].row = cells[i].row + 1;
21287             }
21288             
21289             if (!crow) {
21290                 crow = {
21291                     start : cells[i],
21292                     end :  cells[i]
21293                 };
21294                 continue;
21295             }
21296             if (crow.start.getY() == cells[i].getY()) {
21297                 // on same row.
21298                 crow.end = cells[i];
21299                 continue;
21300             }
21301             // different row.
21302             rows.push(crow);
21303             crow = {
21304                 start: cells[i],
21305                 end : cells[i]
21306             };
21307             
21308         }
21309         
21310         rows.push(crow);
21311         ev.els = [];
21312         ev.rows = rows;
21313         ev.cells = cells;
21314         
21315         cells[0].events.push(ev);
21316         
21317         this.calevents.push(ev);
21318     },
21319     
21320     clearEvents: function() {
21321         
21322         if(!this.calevents){
21323             return;
21324         }
21325         
21326         Roo.each(this.cells.elements, function(c){
21327             c.row = 0;
21328             c.events = [];
21329             c.more = [];
21330         });
21331         
21332         Roo.each(this.calevents, function(e) {
21333             Roo.each(e.els, function(el) {
21334                 el.un('mouseenter' ,this.onEventEnter, this);
21335                 el.un('mouseleave' ,this.onEventLeave, this);
21336                 el.remove();
21337             },this);
21338         },this);
21339         
21340         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21341             e.remove();
21342         });
21343         
21344     },
21345     
21346     renderEvents: function()
21347     {   
21348         var _this = this;
21349         
21350         this.cells.each(function(c) {
21351             
21352             if(c.row < 5){
21353                 return;
21354             }
21355             
21356             var ev = c.events;
21357             
21358             var r = 4;
21359             if(c.row != c.events.length){
21360                 r = 4 - (4 - (c.row - c.events.length));
21361             }
21362             
21363             c.events = ev.slice(0, r);
21364             c.more = ev.slice(r);
21365             
21366             if(c.more.length && c.more.length == 1){
21367                 c.events.push(c.more.pop());
21368             }
21369             
21370             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21371             
21372         });
21373             
21374         this.cells.each(function(c) {
21375             
21376             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21377             
21378             
21379             for (var e = 0; e < c.events.length; e++){
21380                 var ev = c.events[e];
21381                 var rows = ev.rows;
21382                 
21383                 for(var i = 0; i < rows.length; i++) {
21384                 
21385                     // how many rows should it span..
21386
21387                     var  cfg = {
21388                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21389                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21390
21391                         unselectable : "on",
21392                         cn : [
21393                             {
21394                                 cls: 'fc-event-inner',
21395                                 cn : [
21396     //                                {
21397     //                                  tag:'span',
21398     //                                  cls: 'fc-event-time',
21399     //                                  html : cells.length > 1 ? '' : ev.time
21400     //                                },
21401                                     {
21402                                       tag:'span',
21403                                       cls: 'fc-event-title',
21404                                       html : String.format('{0}', ev.title)
21405                                     }
21406
21407
21408                                 ]
21409                             },
21410                             {
21411                                 cls: 'ui-resizable-handle ui-resizable-e',
21412                                 html : '&nbsp;&nbsp;&nbsp'
21413                             }
21414
21415                         ]
21416                     };
21417
21418                     if (i == 0) {
21419                         cfg.cls += ' fc-event-start';
21420                     }
21421                     if ((i+1) == rows.length) {
21422                         cfg.cls += ' fc-event-end';
21423                     }
21424
21425                     var ctr = _this.el.select('.fc-event-container',true).first();
21426                     var cg = ctr.createChild(cfg);
21427
21428                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21429                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21430
21431                     var r = (c.more.length) ? 1 : 0;
21432                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21433                     cg.setWidth(ebox.right - sbox.x -2);
21434
21435                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21436                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21437                     cg.on('click', _this.onEventClick, _this, ev);
21438
21439                     ev.els.push(cg);
21440                     
21441                 }
21442                 
21443             }
21444             
21445             
21446             if(c.more.length){
21447                 var  cfg = {
21448                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21449                     style : 'position: absolute',
21450                     unselectable : "on",
21451                     cn : [
21452                         {
21453                             cls: 'fc-event-inner',
21454                             cn : [
21455                                 {
21456                                   tag:'span',
21457                                   cls: 'fc-event-title',
21458                                   html : 'More'
21459                                 }
21460
21461
21462                             ]
21463                         },
21464                         {
21465                             cls: 'ui-resizable-handle ui-resizable-e',
21466                             html : '&nbsp;&nbsp;&nbsp'
21467                         }
21468
21469                     ]
21470                 };
21471
21472                 var ctr = _this.el.select('.fc-event-container',true).first();
21473                 var cg = ctr.createChild(cfg);
21474
21475                 var sbox = c.select('.fc-day-content',true).first().getBox();
21476                 var ebox = c.select('.fc-day-content',true).first().getBox();
21477                 //Roo.log(cg);
21478                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21479                 cg.setWidth(ebox.right - sbox.x -2);
21480
21481                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21482                 
21483             }
21484             
21485         });
21486         
21487         
21488         
21489     },
21490     
21491     onEventEnter: function (e, el,event,d) {
21492         this.fireEvent('evententer', this, el, event);
21493     },
21494     
21495     onEventLeave: function (e, el,event,d) {
21496         this.fireEvent('eventleave', this, el, event);
21497     },
21498     
21499     onEventClick: function (e, el,event,d) {
21500         this.fireEvent('eventclick', this, el, event);
21501     },
21502     
21503     onMonthChange: function () {
21504         this.store.load();
21505     },
21506     
21507     onMoreEventClick: function(e, el, more)
21508     {
21509         var _this = this;
21510         
21511         this.calpopover.placement = 'right';
21512         this.calpopover.setTitle('More');
21513         
21514         this.calpopover.setContent('');
21515         
21516         var ctr = this.calpopover.el.select('.popover-content', true).first();
21517         
21518         Roo.each(more, function(m){
21519             var cfg = {
21520                 cls : 'fc-event-hori fc-event-draggable',
21521                 html : m.title
21522             };
21523             var cg = ctr.createChild(cfg);
21524             
21525             cg.on('click', _this.onEventClick, _this, m);
21526         });
21527         
21528         this.calpopover.show(el);
21529         
21530         
21531     },
21532     
21533     onLoad: function () 
21534     {   
21535         this.calevents = [];
21536         var cal = this;
21537         
21538         if(this.store.getCount() > 0){
21539             this.store.data.each(function(d){
21540                cal.addItem({
21541                     id : d.data.id,
21542                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21543                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21544                     time : d.data.start_time,
21545                     title : d.data.title,
21546                     description : d.data.description,
21547                     venue : d.data.venue
21548                 });
21549             });
21550         }
21551         
21552         this.renderEvents();
21553         
21554         if(this.calevents.length && this.loadMask){
21555             this.maskEl.hide();
21556         }
21557     },
21558     
21559     onBeforeLoad: function()
21560     {
21561         this.clearEvents();
21562         if(this.loadMask){
21563             this.maskEl.show();
21564         }
21565     }
21566 });
21567
21568  
21569  /*
21570  * - LGPL
21571  *
21572  * element
21573  * 
21574  */
21575
21576 /**
21577  * @class Roo.bootstrap.Popover
21578  * @extends Roo.bootstrap.Component
21579  * @parent none builder
21580  * @children Roo.bootstrap.Component
21581  * Bootstrap Popover class
21582  * @cfg {String} html contents of the popover   (or false to use children..)
21583  * @cfg {String} title of popover (or false to hide)
21584  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21585  * @cfg {String} trigger click || hover (or false to trigger manually)
21586  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21587  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21588  *      - if false and it has a 'parent' then it will be automatically added to that element
21589  *      - if string - Roo.get  will be called 
21590  * @cfg {Number} delay - delay before showing
21591  
21592  * @constructor
21593  * Create a new Popover
21594  * @param {Object} config The config object
21595  */
21596
21597 Roo.bootstrap.Popover = function(config){
21598     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21599     
21600     this.addEvents({
21601         // raw events
21602          /**
21603          * @event show
21604          * After the popover show
21605          * 
21606          * @param {Roo.bootstrap.Popover} this
21607          */
21608         "show" : true,
21609         /**
21610          * @event hide
21611          * After the popover hide
21612          * 
21613          * @param {Roo.bootstrap.Popover} this
21614          */
21615         "hide" : true
21616     });
21617 };
21618
21619 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21620     
21621     title: false,
21622     html: false,
21623     
21624     placement : 'right',
21625     trigger : 'hover', // hover
21626     modal : false,
21627     delay : 0,
21628     
21629     over: false,
21630     
21631     can_build_overlaid : false,
21632     
21633     maskEl : false, // the mask element
21634     headerEl : false,
21635     contentEl : false,
21636     alignEl : false, // when show is called with an element - this get's stored.
21637     
21638     getChildContainer : function()
21639     {
21640         return this.contentEl;
21641         
21642     },
21643     getPopoverHeader : function()
21644     {
21645         this.title = true; // flag not to hide it..
21646         this.headerEl.addClass('p-0');
21647         return this.headerEl
21648     },
21649     
21650     
21651     getAutoCreate : function(){
21652          
21653         var cfg = {
21654            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21655            style: 'display:block',
21656            cn : [
21657                 {
21658                     cls : 'arrow'
21659                 },
21660                 {
21661                     cls : 'popover-inner ',
21662                     cn : [
21663                         {
21664                             tag: 'h3',
21665                             cls: 'popover-title popover-header',
21666                             html : this.title === false ? '' : this.title
21667                         },
21668                         {
21669                             cls : 'popover-content popover-body '  + (this.cls || ''),
21670                             html : this.html || ''
21671                         }
21672                     ]
21673                     
21674                 }
21675            ]
21676         };
21677         
21678         return cfg;
21679     },
21680     /**
21681      * @param {string} the title
21682      */
21683     setTitle: function(str)
21684     {
21685         this.title = str;
21686         if (this.el) {
21687             this.headerEl.dom.innerHTML = str;
21688         }
21689         
21690     },
21691     /**
21692      * @param {string} the body content
21693      */
21694     setContent: function(str)
21695     {
21696         this.html = str;
21697         if (this.contentEl) {
21698             this.contentEl.dom.innerHTML = str;
21699         }
21700         
21701     },
21702     // as it get's added to the bottom of the page.
21703     onRender : function(ct, position)
21704     {
21705         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21706         
21707         
21708         
21709         if(!this.el){
21710             var cfg = Roo.apply({},  this.getAutoCreate());
21711             cfg.id = Roo.id();
21712             
21713             if (this.cls) {
21714                 cfg.cls += ' ' + this.cls;
21715             }
21716             if (this.style) {
21717                 cfg.style = this.style;
21718             }
21719             //Roo.log("adding to ");
21720             this.el = Roo.get(document.body).createChild(cfg, position);
21721 //            Roo.log(this.el);
21722         }
21723         
21724         this.contentEl = this.el.select('.popover-content',true).first();
21725         this.headerEl =  this.el.select('.popover-title',true).first();
21726         
21727         var nitems = [];
21728         if(typeof(this.items) != 'undefined'){
21729             var items = this.items;
21730             delete this.items;
21731
21732             for(var i =0;i < items.length;i++) {
21733                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21734             }
21735         }
21736
21737         this.items = nitems;
21738         
21739         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21740         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21741         
21742         
21743         
21744         this.initEvents();
21745     },
21746     
21747     resizeMask : function()
21748     {
21749         this.maskEl.setSize(
21750             Roo.lib.Dom.getViewWidth(true),
21751             Roo.lib.Dom.getViewHeight(true)
21752         );
21753     },
21754     
21755     initEvents : function()
21756     {
21757         
21758         if (!this.modal) { 
21759             Roo.bootstrap.Popover.register(this);
21760         }
21761          
21762         this.arrowEl = this.el.select('.arrow',true).first();
21763         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21764         this.el.enableDisplayMode('block');
21765         this.el.hide();
21766  
21767         
21768         if (this.over === false && !this.parent()) {
21769             return; 
21770         }
21771         if (this.triggers === false) {
21772             return;
21773         }
21774          
21775         // support parent
21776         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21777         var triggers = this.trigger ? this.trigger.split(' ') : [];
21778         Roo.each(triggers, function(trigger) {
21779         
21780             if (trigger == 'click') {
21781                 on_el.on('click', this.toggle, this);
21782             } else if (trigger != 'manual') {
21783                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21784                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21785       
21786                 on_el.on(eventIn  ,this.enter, this);
21787                 on_el.on(eventOut, this.leave, this);
21788             }
21789         }, this);
21790     },
21791     
21792     
21793     // private
21794     timeout : null,
21795     hoverState : null,
21796     
21797     toggle : function () {
21798         this.hoverState == 'in' ? this.leave() : this.enter();
21799     },
21800     
21801     enter : function () {
21802         
21803         clearTimeout(this.timeout);
21804     
21805         this.hoverState = 'in';
21806     
21807         if (!this.delay || !this.delay.show) {
21808             this.show();
21809             return;
21810         }
21811         var _t = this;
21812         this.timeout = setTimeout(function () {
21813             if (_t.hoverState == 'in') {
21814                 _t.show();
21815             }
21816         }, this.delay.show)
21817     },
21818     
21819     leave : function() {
21820         clearTimeout(this.timeout);
21821     
21822         this.hoverState = 'out';
21823     
21824         if (!this.delay || !this.delay.hide) {
21825             this.hide();
21826             return;
21827         }
21828         var _t = this;
21829         this.timeout = setTimeout(function () {
21830             if (_t.hoverState == 'out') {
21831                 _t.hide();
21832             }
21833         }, this.delay.hide)
21834     },
21835     
21836     /**
21837      * update the position of the dialog
21838      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21839      * 
21840      *
21841      */
21842     
21843     doAlign : function()
21844     {
21845         
21846         if (this.alignEl) {
21847             this.updatePosition(this.placement, true);
21848              
21849         } else {
21850             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21851             var es = this.el.getSize();
21852             var x = Roo.lib.Dom.getViewWidth()/2;
21853             var y = Roo.lib.Dom.getViewHeight()/2;
21854             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21855             
21856         }
21857
21858          
21859          
21860         
21861         
21862     },
21863     
21864     /**
21865      * Show the popover
21866      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21867      * @param {string} (left|right|top|bottom) position
21868      */
21869     show : function (on_el, placement)
21870     {
21871         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21872         on_el = on_el || false; // default to false
21873          
21874         if (!on_el) {
21875             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21876                 on_el = this.parent().el;
21877             } else if (this.over) {
21878                 on_el = Roo.get(this.over);
21879             }
21880             
21881         }
21882         
21883         this.alignEl = Roo.get( on_el );
21884
21885         if (!this.el) {
21886             this.render(document.body);
21887         }
21888         
21889         
21890          
21891         
21892         if (this.title === false) {
21893             this.headerEl.hide();
21894         }
21895         
21896        
21897         this.el.show();
21898         this.el.dom.style.display = 'block';
21899          
21900         this.doAlign();
21901         
21902         //var arrow = this.el.select('.arrow',true).first();
21903         //arrow.set(align[2], 
21904         
21905         this.el.addClass('in');
21906         
21907          
21908         
21909         this.hoverState = 'in';
21910         
21911         if (this.modal) {
21912             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21913             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21914             this.maskEl.dom.style.display = 'block';
21915             this.maskEl.addClass('show');
21916         }
21917         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21918  
21919         this.fireEvent('show', this);
21920         
21921     },
21922     /**
21923      * fire this manually after loading a grid in the table for example
21924      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21925      * @param {Boolean} try and move it if we cant get right position.
21926      */
21927     updatePosition : function(placement, try_move)
21928     {
21929         // allow for calling with no parameters
21930         placement = placement   ? placement :  this.placement;
21931         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21932         
21933         this.el.removeClass([
21934             'fade','top','bottom', 'left', 'right','in',
21935             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21936         ]);
21937         this.el.addClass(placement + ' bs-popover-' + placement);
21938         
21939         if (!this.alignEl ) {
21940             return false;
21941         }
21942         
21943         switch (placement) {
21944             case 'right':
21945                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21946                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21947                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21948                     //normal display... or moved up/down.
21949                     this.el.setXY(offset);
21950                     var xy = this.alignEl.getAnchorXY('tr', false);
21951                     xy[0]+=2;xy[1]+=5;
21952                     this.arrowEl.setXY(xy);
21953                     return true;
21954                 }
21955                 // continue through...
21956                 return this.updatePosition('left', false);
21957                 
21958             
21959             case 'left':
21960                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21961                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21962                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21963                     //normal display... or moved up/down.
21964                     this.el.setXY(offset);
21965                     var xy = this.alignEl.getAnchorXY('tl', false);
21966                     xy[0]-=10;xy[1]+=5; // << fix me
21967                     this.arrowEl.setXY(xy);
21968                     return true;
21969                 }
21970                 // call self...
21971                 return this.updatePosition('right', false);
21972             
21973             case 'top':
21974                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21975                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21976                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21977                     //normal display... or moved up/down.
21978                     this.el.setXY(offset);
21979                     var xy = this.alignEl.getAnchorXY('t', false);
21980                     xy[1]-=10; // << fix me
21981                     this.arrowEl.setXY(xy);
21982                     return true;
21983                 }
21984                 // fall through
21985                return this.updatePosition('bottom', false);
21986             
21987             case 'bottom':
21988                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21989                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21990                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21991                     //normal display... or moved up/down.
21992                     this.el.setXY(offset);
21993                     var xy = this.alignEl.getAnchorXY('b', false);
21994                      xy[1]+=2; // << fix me
21995                     this.arrowEl.setXY(xy);
21996                     return true;
21997                 }
21998                 // fall through
21999                 return this.updatePosition('top', false);
22000                 
22001             
22002         }
22003         
22004         
22005         return false;
22006     },
22007     
22008     hide : function()
22009     {
22010         this.el.setXY([0,0]);
22011         this.el.removeClass('in');
22012         this.el.hide();
22013         this.hoverState = null;
22014         this.maskEl.hide(); // always..
22015         this.fireEvent('hide', this);
22016     }
22017     
22018 });
22019
22020
22021 Roo.apply(Roo.bootstrap.Popover, {
22022
22023     alignment : {
22024         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
22025         'right' : ['l-br', [10,0], 'right bs-popover-right'],
22026         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
22027         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
22028     },
22029     
22030     zIndex : 20001,
22031
22032     clickHander : false,
22033     
22034     
22035
22036     onMouseDown : function(e)
22037     {
22038         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22039             /// what is nothing is showing..
22040             this.hideAll();
22041         }
22042          
22043     },
22044     
22045     
22046     popups : [],
22047     
22048     register : function(popup)
22049     {
22050         if (!Roo.bootstrap.Popover.clickHandler) {
22051             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22052         }
22053         // hide other popups.
22054         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22055         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22056         this.hideAll(); //<< why?
22057         //this.popups.push(popup);
22058     },
22059     hideAll : function()
22060     {
22061         this.popups.forEach(function(p) {
22062             p.hide();
22063         });
22064     },
22065     onShow : function() {
22066         Roo.bootstrap.Popover.popups.push(this);
22067     },
22068     onHide : function() {
22069         Roo.bootstrap.Popover.popups.remove(this);
22070     } 
22071
22072 });
22073 /**
22074  * @class Roo.bootstrap.PopoverNav
22075  * @extends Roo.bootstrap.nav.Simplebar
22076  * @parent Roo.bootstrap.Popover
22077  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22078  * @licence LGPL
22079  * Bootstrap Popover header navigation class
22080  * FIXME? should this go under nav?
22081  *
22082  * 
22083  * @constructor
22084  * Create a new Popover Header Navigation 
22085  * @param {Object} config The config object
22086  */
22087
22088 Roo.bootstrap.PopoverNav = function(config){
22089     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22090 };
22091
22092 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22093     
22094     
22095     container_method : 'getPopoverHeader' 
22096     
22097      
22098     
22099     
22100    
22101 });
22102
22103  
22104
22105  /*
22106  * - LGPL
22107  *
22108  * Progress
22109  * 
22110  */
22111
22112 /**
22113  * @class Roo.bootstrap.Progress
22114  * @extends Roo.bootstrap.Component
22115  * @children Roo.bootstrap.ProgressBar
22116  * Bootstrap Progress class
22117  * @cfg {Boolean} striped striped of the progress bar
22118  * @cfg {Boolean} active animated of the progress bar
22119  * 
22120  * 
22121  * @constructor
22122  * Create a new Progress
22123  * @param {Object} config The config object
22124  */
22125
22126 Roo.bootstrap.Progress = function(config){
22127     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22128 };
22129
22130 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22131     
22132     striped : false,
22133     active: false,
22134     
22135     getAutoCreate : function(){
22136         var cfg = {
22137             tag: 'div',
22138             cls: 'progress'
22139         };
22140         
22141         
22142         if(this.striped){
22143             cfg.cls += ' progress-striped';
22144         }
22145       
22146         if(this.active){
22147             cfg.cls += ' active';
22148         }
22149         
22150         
22151         return cfg;
22152     }
22153    
22154 });
22155
22156  
22157
22158  /*
22159  * - LGPL
22160  *
22161  * ProgressBar
22162  * 
22163  */
22164
22165 /**
22166  * @class Roo.bootstrap.ProgressBar
22167  * @extends Roo.bootstrap.Component
22168  * Bootstrap ProgressBar class
22169  * @cfg {Number} aria_valuenow aria-value now
22170  * @cfg {Number} aria_valuemin aria-value min
22171  * @cfg {Number} aria_valuemax aria-value max
22172  * @cfg {String} label label for the progress bar
22173  * @cfg {String} panel (success | info | warning | danger )
22174  * @cfg {String} role role of the progress bar
22175  * @cfg {String} sr_only text
22176  * 
22177  * 
22178  * @constructor
22179  * Create a new ProgressBar
22180  * @param {Object} config The config object
22181  */
22182
22183 Roo.bootstrap.ProgressBar = function(config){
22184     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22185 };
22186
22187 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22188     
22189     aria_valuenow : 0,
22190     aria_valuemin : 0,
22191     aria_valuemax : 100,
22192     label : false,
22193     panel : false,
22194     role : false,
22195     sr_only: false,
22196     
22197     getAutoCreate : function()
22198     {
22199         
22200         var cfg = {
22201             tag: 'div',
22202             cls: 'progress-bar',
22203             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22204         };
22205         
22206         if(this.sr_only){
22207             cfg.cn = {
22208                 tag: 'span',
22209                 cls: 'sr-only',
22210                 html: this.sr_only
22211             }
22212         }
22213         
22214         if(this.role){
22215             cfg.role = this.role;
22216         }
22217         
22218         if(this.aria_valuenow){
22219             cfg['aria-valuenow'] = this.aria_valuenow;
22220         }
22221         
22222         if(this.aria_valuemin){
22223             cfg['aria-valuemin'] = this.aria_valuemin;
22224         }
22225         
22226         if(this.aria_valuemax){
22227             cfg['aria-valuemax'] = this.aria_valuemax;
22228         }
22229         
22230         if(this.label && !this.sr_only){
22231             cfg.html = this.label;
22232         }
22233         
22234         if(this.panel){
22235             cfg.cls += ' progress-bar-' + this.panel;
22236         }
22237         
22238         return cfg;
22239     },
22240     
22241     update : function(aria_valuenow)
22242     {
22243         this.aria_valuenow = aria_valuenow;
22244         
22245         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22246     }
22247    
22248 });
22249
22250  
22251
22252  /**
22253  * @class Roo.bootstrap.TabGroup
22254  * @extends Roo.bootstrap.Column
22255  * @children Roo.bootstrap.TabPanel
22256  * Bootstrap Column class
22257  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22258  * @cfg {Boolean} carousel true to make the group behave like a carousel
22259  * @cfg {Boolean} bullets show bullets for the panels
22260  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22261  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22262  * @cfg {Boolean} showarrow (true|false) show arrow default true
22263  * 
22264  * @constructor
22265  * Create a new TabGroup
22266  * @param {Object} config The config object
22267  */
22268
22269 Roo.bootstrap.TabGroup = function(config){
22270     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22271     if (!this.navId) {
22272         this.navId = Roo.id();
22273     }
22274     this.tabs = [];
22275     Roo.bootstrap.TabGroup.register(this);
22276     
22277 };
22278
22279 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22280     
22281     carousel : false,
22282     transition : false,
22283     bullets : 0,
22284     timer : 0,
22285     autoslide : false,
22286     slideFn : false,
22287     slideOnTouch : false,
22288     showarrow : true,
22289     
22290     getAutoCreate : function()
22291     {
22292         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22293         
22294         cfg.cls += ' tab-content';
22295         
22296         if (this.carousel) {
22297             cfg.cls += ' carousel slide';
22298             
22299             cfg.cn = [{
22300                cls : 'carousel-inner',
22301                cn : []
22302             }];
22303         
22304             if(this.bullets  && !Roo.isTouch){
22305                 
22306                 var bullets = {
22307                     cls : 'carousel-bullets',
22308                     cn : []
22309                 };
22310                
22311                 if(this.bullets_cls){
22312                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22313                 }
22314                 
22315                 bullets.cn.push({
22316                     cls : 'clear'
22317                 });
22318                 
22319                 cfg.cn[0].cn.push(bullets);
22320             }
22321             
22322             if(this.showarrow){
22323                 cfg.cn[0].cn.push({
22324                     tag : 'div',
22325                     class : 'carousel-arrow',
22326                     cn : [
22327                         {
22328                             tag : 'div',
22329                             class : 'carousel-prev',
22330                             cn : [
22331                                 {
22332                                     tag : 'i',
22333                                     class : 'fa fa-chevron-left'
22334                                 }
22335                             ]
22336                         },
22337                         {
22338                             tag : 'div',
22339                             class : 'carousel-next',
22340                             cn : [
22341                                 {
22342                                     tag : 'i',
22343                                     class : 'fa fa-chevron-right'
22344                                 }
22345                             ]
22346                         }
22347                     ]
22348                 });
22349             }
22350             
22351         }
22352         
22353         return cfg;
22354     },
22355     
22356     initEvents:  function()
22357     {
22358 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22359 //            this.el.on("touchstart", this.onTouchStart, this);
22360 //        }
22361         
22362         if(this.autoslide){
22363             var _this = this;
22364             
22365             this.slideFn = window.setInterval(function() {
22366                 _this.showPanelNext();
22367             }, this.timer);
22368         }
22369         
22370         if(this.showarrow){
22371             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22372             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22373         }
22374         
22375         
22376     },
22377     
22378 //    onTouchStart : function(e, el, o)
22379 //    {
22380 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22381 //            return;
22382 //        }
22383 //        
22384 //        this.showPanelNext();
22385 //    },
22386     
22387     
22388     getChildContainer : function()
22389     {
22390         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22391     },
22392     
22393     /**
22394     * register a Navigation item
22395     * @param {Roo.bootstrap.nav.Item} the navitem to add
22396     */
22397     register : function(item)
22398     {
22399         this.tabs.push( item);
22400         item.navId = this.navId; // not really needed..
22401         this.addBullet();
22402     
22403     },
22404     
22405     getActivePanel : function()
22406     {
22407         var r = false;
22408         Roo.each(this.tabs, function(t) {
22409             if (t.active) {
22410                 r = t;
22411                 return false;
22412             }
22413             return null;
22414         });
22415         return r;
22416         
22417     },
22418     getPanelByName : function(n)
22419     {
22420         var r = false;
22421         Roo.each(this.tabs, function(t) {
22422             if (t.tabId == n) {
22423                 r = t;
22424                 return false;
22425             }
22426             return null;
22427         });
22428         return r;
22429     },
22430     indexOfPanel : function(p)
22431     {
22432         var r = false;
22433         Roo.each(this.tabs, function(t,i) {
22434             if (t.tabId == p.tabId) {
22435                 r = i;
22436                 return false;
22437             }
22438             return null;
22439         });
22440         return r;
22441     },
22442     /**
22443      * show a specific panel
22444      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22445      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22446      */
22447     showPanel : function (pan)
22448     {
22449         if(this.transition || typeof(pan) == 'undefined'){
22450             Roo.log("waiting for the transitionend");
22451             return false;
22452         }
22453         
22454         if (typeof(pan) == 'number') {
22455             pan = this.tabs[pan];
22456         }
22457         
22458         if (typeof(pan) == 'string') {
22459             pan = this.getPanelByName(pan);
22460         }
22461         
22462         var cur = this.getActivePanel();
22463         
22464         if(!pan || !cur){
22465             Roo.log('pan or acitve pan is undefined');
22466             return false;
22467         }
22468         
22469         if (pan.tabId == this.getActivePanel().tabId) {
22470             return true;
22471         }
22472         
22473         if (false === cur.fireEvent('beforedeactivate')) {
22474             return false;
22475         }
22476         
22477         if(this.bullets > 0 && !Roo.isTouch){
22478             this.setActiveBullet(this.indexOfPanel(pan));
22479         }
22480         
22481         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22482             
22483             //class="carousel-item carousel-item-next carousel-item-left"
22484             
22485             this.transition = true;
22486             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22487             var lr = dir == 'next' ? 'left' : 'right';
22488             pan.el.addClass(dir); // or prev
22489             pan.el.addClass('carousel-item-' + dir); // or prev
22490             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22491             cur.el.addClass(lr); // or right
22492             pan.el.addClass(lr);
22493             cur.el.addClass('carousel-item-' +lr); // or right
22494             pan.el.addClass('carousel-item-' +lr);
22495             
22496             
22497             var _this = this;
22498             cur.el.on('transitionend', function() {
22499                 Roo.log("trans end?");
22500                 
22501                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22502                 pan.setActive(true);
22503                 
22504                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22505                 cur.setActive(false);
22506                 
22507                 _this.transition = false;
22508                 
22509             }, this, { single:  true } );
22510             
22511             return true;
22512         }
22513         
22514         cur.setActive(false);
22515         pan.setActive(true);
22516         
22517         return true;
22518         
22519     },
22520     showPanelNext : function()
22521     {
22522         var i = this.indexOfPanel(this.getActivePanel());
22523         
22524         if (i >= this.tabs.length - 1 && !this.autoslide) {
22525             return;
22526         }
22527         
22528         if (i >= this.tabs.length - 1 && this.autoslide) {
22529             i = -1;
22530         }
22531         
22532         this.showPanel(this.tabs[i+1]);
22533     },
22534     
22535     showPanelPrev : function()
22536     {
22537         var i = this.indexOfPanel(this.getActivePanel());
22538         
22539         if (i  < 1 && !this.autoslide) {
22540             return;
22541         }
22542         
22543         if (i < 1 && this.autoslide) {
22544             i = this.tabs.length;
22545         }
22546         
22547         this.showPanel(this.tabs[i-1]);
22548     },
22549     
22550     
22551     addBullet: function()
22552     {
22553         if(!this.bullets || Roo.isTouch){
22554             return;
22555         }
22556         var ctr = this.el.select('.carousel-bullets',true).first();
22557         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22558         var bullet = ctr.createChild({
22559             cls : 'bullet bullet-' + i
22560         },ctr.dom.lastChild);
22561         
22562         
22563         var _this = this;
22564         
22565         bullet.on('click', (function(e, el, o, ii, t){
22566
22567             e.preventDefault();
22568
22569             this.showPanel(ii);
22570
22571             if(this.autoslide && this.slideFn){
22572                 clearInterval(this.slideFn);
22573                 this.slideFn = window.setInterval(function() {
22574                     _this.showPanelNext();
22575                 }, this.timer);
22576             }
22577
22578         }).createDelegate(this, [i, bullet], true));
22579                 
22580         
22581     },
22582      
22583     setActiveBullet : function(i)
22584     {
22585         if(Roo.isTouch){
22586             return;
22587         }
22588         
22589         Roo.each(this.el.select('.bullet', true).elements, function(el){
22590             el.removeClass('selected');
22591         });
22592
22593         var bullet = this.el.select('.bullet-' + i, true).first();
22594         
22595         if(!bullet){
22596             return;
22597         }
22598         
22599         bullet.addClass('selected');
22600     }
22601     
22602     
22603   
22604 });
22605
22606  
22607
22608  
22609  
22610 Roo.apply(Roo.bootstrap.TabGroup, {
22611     
22612     groups: {},
22613      /**
22614     * register a Navigation Group
22615     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22616     */
22617     register : function(navgrp)
22618     {
22619         this.groups[navgrp.navId] = navgrp;
22620         
22621     },
22622     /**
22623     * fetch a Navigation Group based on the navigation ID
22624     * if one does not exist , it will get created.
22625     * @param {string} the navgroup to add
22626     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22627     */
22628     get: function(navId) {
22629         if (typeof(this.groups[navId]) == 'undefined') {
22630             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22631         }
22632         return this.groups[navId] ;
22633     }
22634     
22635     
22636     
22637 });
22638
22639  /*
22640  * - LGPL
22641  *
22642  * TabPanel
22643  * 
22644  */
22645
22646 /**
22647  * @class Roo.bootstrap.TabPanel
22648  * @extends Roo.bootstrap.Component
22649  * @children Roo.bootstrap.Component
22650  * Bootstrap TabPanel class
22651  * @cfg {Boolean} active panel active
22652  * @cfg {String} html panel content
22653  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22654  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22655  * @cfg {String} href click to link..
22656  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22657  * 
22658  * 
22659  * @constructor
22660  * Create a new TabPanel
22661  * @param {Object} config The config object
22662  */
22663
22664 Roo.bootstrap.TabPanel = function(config){
22665     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22666     this.addEvents({
22667         /**
22668              * @event changed
22669              * Fires when the active status changes
22670              * @param {Roo.bootstrap.TabPanel} this
22671              * @param {Boolean} state the new state
22672             
22673          */
22674         'changed': true,
22675         /**
22676              * @event beforedeactivate
22677              * Fires before a tab is de-activated - can be used to do validation on a form.
22678              * @param {Roo.bootstrap.TabPanel} this
22679              * @return {Boolean} false if there is an error
22680             
22681          */
22682         'beforedeactivate': true
22683      });
22684     
22685     this.tabId = this.tabId || Roo.id();
22686   
22687 };
22688
22689 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22690     
22691     active: false,
22692     html: false,
22693     tabId: false,
22694     navId : false,
22695     href : '',
22696     touchSlide : false,
22697     getAutoCreate : function(){
22698         
22699         
22700         var cfg = {
22701             tag: 'div',
22702             // item is needed for carousel - not sure if it has any effect otherwise
22703             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22704             html: this.html || ''
22705         };
22706         
22707         if(this.active){
22708             cfg.cls += ' active';
22709         }
22710         
22711         if(this.tabId){
22712             cfg.tabId = this.tabId;
22713         }
22714         
22715         
22716         
22717         return cfg;
22718     },
22719     
22720     initEvents:  function()
22721     {
22722         var p = this.parent();
22723         
22724         this.navId = this.navId || p.navId;
22725         
22726         if (typeof(this.navId) != 'undefined') {
22727             // not really needed.. but just in case.. parent should be a NavGroup.
22728             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22729             
22730             tg.register(this);
22731             
22732             var i = tg.tabs.length - 1;
22733             
22734             if(this.active && tg.bullets > 0 && i < tg.bullets){
22735                 tg.setActiveBullet(i);
22736             }
22737         }
22738         
22739         this.el.on('click', this.onClick, this);
22740         
22741         if(Roo.isTouch && this.touchSlide){
22742             this.el.on("touchstart", this.onTouchStart, this);
22743             this.el.on("touchmove", this.onTouchMove, this);
22744             this.el.on("touchend", this.onTouchEnd, this);
22745         }
22746         
22747     },
22748     
22749     onRender : function(ct, position)
22750     {
22751         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22752     },
22753     
22754     setActive : function(state)
22755     {
22756         Roo.log("panel - set active " + this.tabId + "=" + state);
22757         
22758         this.active = state;
22759         if (!state) {
22760             this.el.removeClass('active');
22761             
22762         } else  if (!this.el.hasClass('active')) {
22763             this.el.addClass('active');
22764         }
22765         
22766         this.fireEvent('changed', this, state);
22767     },
22768     
22769     onClick : function(e)
22770     {
22771         e.preventDefault();
22772         
22773         if(!this.href.length){
22774             return;
22775         }
22776         
22777         window.location.href = this.href;
22778     },
22779     
22780     startX : 0,
22781     startY : 0,
22782     endX : 0,
22783     endY : 0,
22784     swiping : false,
22785     
22786     onTouchStart : function(e)
22787     {
22788         this.swiping = false;
22789         
22790         this.startX = e.browserEvent.touches[0].clientX;
22791         this.startY = e.browserEvent.touches[0].clientY;
22792     },
22793     
22794     onTouchMove : function(e)
22795     {
22796         this.swiping = true;
22797         
22798         this.endX = e.browserEvent.touches[0].clientX;
22799         this.endY = e.browserEvent.touches[0].clientY;
22800     },
22801     
22802     onTouchEnd : function(e)
22803     {
22804         if(!this.swiping){
22805             this.onClick(e);
22806             return;
22807         }
22808         
22809         var tabGroup = this.parent();
22810         
22811         if(this.endX > this.startX){ // swiping right
22812             tabGroup.showPanelPrev();
22813             return;
22814         }
22815         
22816         if(this.startX > this.endX){ // swiping left
22817             tabGroup.showPanelNext();
22818             return;
22819         }
22820     }
22821     
22822     
22823 });
22824  
22825
22826  
22827
22828  /*
22829  * - LGPL
22830  *
22831  * DateField
22832  * 
22833  */
22834
22835 /**
22836  * @class Roo.bootstrap.form.DateField
22837  * @extends Roo.bootstrap.form.Input
22838  * Bootstrap DateField class
22839  * @cfg {Number} weekStart default 0
22840  * @cfg {String} viewMode default empty, (months|years)
22841  * @cfg {String} minViewMode default empty, (months|years)
22842  * @cfg {Number} startDate default -Infinity
22843  * @cfg {Number} endDate default Infinity
22844  * @cfg {Boolean} todayHighlight default false
22845  * @cfg {Boolean} todayBtn default false
22846  * @cfg {Boolean} calendarWeeks default false
22847  * @cfg {Object} daysOfWeekDisabled default empty
22848  * @cfg {Boolean} singleMode default false (true | false)
22849  * 
22850  * @cfg {Boolean} keyboardNavigation default true
22851  * @cfg {String} language default en
22852  * 
22853  * @constructor
22854  * Create a new DateField
22855  * @param {Object} config The config object
22856  */
22857
22858 Roo.bootstrap.form.DateField = function(config){
22859     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22860      this.addEvents({
22861             /**
22862              * @event show
22863              * Fires when this field show.
22864              * @param {Roo.bootstrap.form.DateField} this
22865              * @param {Mixed} date The date value
22866              */
22867             show : true,
22868             /**
22869              * @event show
22870              * Fires when this field hide.
22871              * @param {Roo.bootstrap.form.DateField} this
22872              * @param {Mixed} date The date value
22873              */
22874             hide : true,
22875             /**
22876              * @event select
22877              * Fires when select a date.
22878              * @param {Roo.bootstrap.form.DateField} this
22879              * @param {Mixed} date The date value
22880              */
22881             select : true,
22882             /**
22883              * @event beforeselect
22884              * Fires when before select a date.
22885              * @param {Roo.bootstrap.form.DateField} this
22886              * @param {Mixed} date The date value
22887              */
22888             beforeselect : true
22889         });
22890 };
22891
22892 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22893     
22894     /**
22895      * @cfg {String} format
22896      * The default date format string which can be overriden for localization support.  The format must be
22897      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22898      */
22899     format : "m/d/y",
22900     /**
22901      * @cfg {String} altFormats
22902      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22903      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22904      */
22905     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22906     
22907     weekStart : 0,
22908     
22909     viewMode : '',
22910     
22911     minViewMode : '',
22912     
22913     todayHighlight : false,
22914     
22915     todayBtn: false,
22916     
22917     language: 'en',
22918     
22919     keyboardNavigation: true,
22920     
22921     calendarWeeks: false,
22922     
22923     startDate: -Infinity,
22924     
22925     endDate: Infinity,
22926     
22927     daysOfWeekDisabled: [],
22928     
22929     _events: [],
22930     
22931     singleMode : false,
22932     
22933     UTCDate: function()
22934     {
22935         return new Date(Date.UTC.apply(Date, arguments));
22936     },
22937     
22938     UTCToday: function()
22939     {
22940         var today = new Date();
22941         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22942     },
22943     
22944     getDate: function() {
22945             var d = this.getUTCDate();
22946             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22947     },
22948     
22949     getUTCDate: function() {
22950             return this.date;
22951     },
22952     
22953     setDate: function(d) {
22954             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22955     },
22956     
22957     setUTCDate: function(d) {
22958             this.date = d;
22959             this.setValue(this.formatDate(this.date));
22960     },
22961         
22962     onRender: function(ct, position)
22963     {
22964         
22965         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22966         
22967         this.language = this.language || 'en';
22968         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22969         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22970         
22971         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22972         this.format = this.format || 'm/d/y';
22973         this.isInline = false;
22974         this.isInput = true;
22975         this.component = this.el.select('.add-on', true).first() || false;
22976         this.component = (this.component && this.component.length === 0) ? false : this.component;
22977         this.hasInput = this.component && this.inputEl().length;
22978         
22979         if (typeof(this.minViewMode === 'string')) {
22980             switch (this.minViewMode) {
22981                 case 'months':
22982                     this.minViewMode = 1;
22983                     break;
22984                 case 'years':
22985                     this.minViewMode = 2;
22986                     break;
22987                 default:
22988                     this.minViewMode = 0;
22989                     break;
22990             }
22991         }
22992         
22993         if (typeof(this.viewMode === 'string')) {
22994             switch (this.viewMode) {
22995                 case 'months':
22996                     this.viewMode = 1;
22997                     break;
22998                 case 'years':
22999                     this.viewMode = 2;
23000                     break;
23001                 default:
23002                     this.viewMode = 0;
23003                     break;
23004             }
23005         }
23006                 
23007         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
23008         
23009 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
23010         
23011         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23012         
23013         this.picker().on('mousedown', this.onMousedown, this);
23014         this.picker().on('click', this.onClick, this);
23015         
23016         this.picker().addClass('datepicker-dropdown');
23017         
23018         this.startViewMode = this.viewMode;
23019         
23020         if(this.singleMode){
23021             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
23022                 v.setVisibilityMode(Roo.Element.DISPLAY);
23023                 v.hide();
23024             });
23025             
23026             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23027                 v.setStyle('width', '189px');
23028             });
23029         }
23030         
23031         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23032             if(!this.calendarWeeks){
23033                 v.remove();
23034                 return;
23035             }
23036             
23037             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23038             v.attr('colspan', function(i, val){
23039                 return parseInt(val) + 1;
23040             });
23041         });
23042                         
23043         
23044         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23045         
23046         this.setStartDate(this.startDate);
23047         this.setEndDate(this.endDate);
23048         
23049         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23050         
23051         this.fillDow();
23052         this.fillMonths();
23053         this.update();
23054         this.showMode();
23055         
23056         if(this.isInline) {
23057             this.showPopup();
23058         }
23059     },
23060     
23061     picker : function()
23062     {
23063         return this.pickerEl;
23064 //        return this.el.select('.datepicker', true).first();
23065     },
23066     
23067     fillDow: function()
23068     {
23069         var dowCnt = this.weekStart;
23070         
23071         var dow = {
23072             tag: 'tr',
23073             cn: [
23074                 
23075             ]
23076         };
23077         
23078         if(this.calendarWeeks){
23079             dow.cn.push({
23080                 tag: 'th',
23081                 cls: 'cw',
23082                 html: '&nbsp;'
23083             })
23084         }
23085         
23086         while (dowCnt < this.weekStart + 7) {
23087             dow.cn.push({
23088                 tag: 'th',
23089                 cls: 'dow',
23090                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23091             });
23092         }
23093         
23094         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23095     },
23096     
23097     fillMonths: function()
23098     {    
23099         var i = 0;
23100         var months = this.picker().select('>.datepicker-months td', true).first();
23101         
23102         months.dom.innerHTML = '';
23103         
23104         while (i < 12) {
23105             var month = {
23106                 tag: 'span',
23107                 cls: 'month',
23108                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23109             };
23110             
23111             months.createChild(month);
23112         }
23113         
23114     },
23115     
23116     update: function()
23117     {
23118         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;
23119         
23120         if (this.date < this.startDate) {
23121             this.viewDate = new Date(this.startDate);
23122         } else if (this.date > this.endDate) {
23123             this.viewDate = new Date(this.endDate);
23124         } else {
23125             this.viewDate = new Date(this.date);
23126         }
23127         
23128         this.fill();
23129     },
23130     
23131     fill: function() 
23132     {
23133         var d = new Date(this.viewDate),
23134                 year = d.getUTCFullYear(),
23135                 month = d.getUTCMonth(),
23136                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23137                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23138                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23139                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23140                 currentDate = this.date && this.date.valueOf(),
23141                 today = this.UTCToday();
23142         
23143         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23144         
23145 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23146         
23147 //        this.picker.select('>tfoot th.today').
23148 //                                              .text(dates[this.language].today)
23149 //                                              .toggle(this.todayBtn !== false);
23150     
23151         this.updateNavArrows();
23152         this.fillMonths();
23153                                                 
23154         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23155         
23156         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23157          
23158         prevMonth.setUTCDate(day);
23159         
23160         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23161         
23162         var nextMonth = new Date(prevMonth);
23163         
23164         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23165         
23166         nextMonth = nextMonth.valueOf();
23167         
23168         var fillMonths = false;
23169         
23170         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23171         
23172         while(prevMonth.valueOf() <= nextMonth) {
23173             var clsName = '';
23174             
23175             if (prevMonth.getUTCDay() === this.weekStart) {
23176                 if(fillMonths){
23177                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23178                 }
23179                     
23180                 fillMonths = {
23181                     tag: 'tr',
23182                     cn: []
23183                 };
23184                 
23185                 if(this.calendarWeeks){
23186                     // ISO 8601: First week contains first thursday.
23187                     // ISO also states week starts on Monday, but we can be more abstract here.
23188                     var
23189                     // Start of current week: based on weekstart/current date
23190                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23191                     // Thursday of this week
23192                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23193                     // First Thursday of year, year from thursday
23194                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23195                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23196                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23197                     
23198                     fillMonths.cn.push({
23199                         tag: 'td',
23200                         cls: 'cw',
23201                         html: calWeek
23202                     });
23203                 }
23204             }
23205             
23206             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23207                 clsName += ' old';
23208             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23209                 clsName += ' new';
23210             }
23211             if (this.todayHighlight &&
23212                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23213                 prevMonth.getUTCMonth() == today.getMonth() &&
23214                 prevMonth.getUTCDate() == today.getDate()) {
23215                 clsName += ' today';
23216             }
23217             
23218             if (currentDate && prevMonth.valueOf() === currentDate) {
23219                 clsName += ' active';
23220             }
23221             
23222             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23223                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23224                     clsName += ' disabled';
23225             }
23226             
23227             fillMonths.cn.push({
23228                 tag: 'td',
23229                 cls: 'day ' + clsName,
23230                 html: prevMonth.getDate()
23231             });
23232             
23233             prevMonth.setDate(prevMonth.getDate()+1);
23234         }
23235           
23236         var currentYear = this.date && this.date.getUTCFullYear();
23237         var currentMonth = this.date && this.date.getUTCMonth();
23238         
23239         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23240         
23241         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23242             v.removeClass('active');
23243             
23244             if(currentYear === year && k === currentMonth){
23245                 v.addClass('active');
23246             }
23247             
23248             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23249                 v.addClass('disabled');
23250             }
23251             
23252         });
23253         
23254         
23255         year = parseInt(year/10, 10) * 10;
23256         
23257         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23258         
23259         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23260         
23261         year -= 1;
23262         for (var i = -1; i < 11; i++) {
23263             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23264                 tag: 'span',
23265                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23266                 html: year
23267             });
23268             
23269             year += 1;
23270         }
23271     },
23272     
23273     showMode: function(dir) 
23274     {
23275         if (dir) {
23276             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23277         }
23278         
23279         Roo.each(this.picker().select('>div',true).elements, function(v){
23280             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23281             v.hide();
23282         });
23283         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23284     },
23285     
23286     place: function()
23287     {
23288         if(this.isInline) {
23289             return;
23290         }
23291         
23292         this.picker().removeClass(['bottom', 'top']);
23293         
23294         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23295             /*
23296              * place to the top of element!
23297              *
23298              */
23299             
23300             this.picker().addClass('top');
23301             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23302             
23303             return;
23304         }
23305         
23306         this.picker().addClass('bottom');
23307         
23308         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23309     },
23310     
23311     parseDate : function(value)
23312     {
23313         if(!value || value instanceof Date){
23314             return value;
23315         }
23316         var v = Date.parseDate(value, this.format);
23317         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23318             v = Date.parseDate(value, 'Y-m-d');
23319         }
23320         if(!v && this.altFormats){
23321             if(!this.altFormatsArray){
23322                 this.altFormatsArray = this.altFormats.split("|");
23323             }
23324             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23325                 v = Date.parseDate(value, this.altFormatsArray[i]);
23326             }
23327         }
23328         return v;
23329     },
23330     
23331     formatDate : function(date, fmt)
23332     {   
23333         return (!date || !(date instanceof Date)) ?
23334         date : date.dateFormat(fmt || this.format);
23335     },
23336     
23337     onFocus : function()
23338     {
23339         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23340         this.showPopup();
23341     },
23342     
23343     onBlur : function()
23344     {
23345         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23346         
23347         var d = this.inputEl().getValue();
23348         
23349         this.setValue(d);
23350                 
23351         this.hidePopup();
23352     },
23353     
23354     showPopup : function()
23355     {
23356         this.picker().show();
23357         this.update();
23358         this.place();
23359         
23360         this.fireEvent('showpopup', this, this.date);
23361     },
23362     
23363     hidePopup : function()
23364     {
23365         if(this.isInline) {
23366             return;
23367         }
23368         this.picker().hide();
23369         this.viewMode = this.startViewMode;
23370         this.showMode();
23371         
23372         this.fireEvent('hidepopup', this, this.date);
23373         
23374     },
23375     
23376     onMousedown: function(e)
23377     {
23378         e.stopPropagation();
23379         e.preventDefault();
23380     },
23381     
23382     keyup: function(e)
23383     {
23384         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23385         this.update();
23386     },
23387
23388     setValue: function(v)
23389     {
23390         if(this.fireEvent('beforeselect', this, v) !== false){
23391             var d = new Date(this.parseDate(v) ).clearTime();
23392         
23393             if(isNaN(d.getTime())){
23394                 this.date = this.viewDate = '';
23395                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23396                 return;
23397             }
23398
23399             v = this.formatDate(d);
23400
23401             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23402
23403             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23404
23405             this.update();
23406
23407             this.fireEvent('select', this, this.date);
23408         }
23409     },
23410     
23411     getValue: function()
23412     {
23413         return this.formatDate(this.date);
23414     },
23415     
23416     fireKey: function(e)
23417     {
23418         if (!this.picker().isVisible()){
23419             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23420                 this.showPopup();
23421             }
23422             return;
23423         }
23424         
23425         var dateChanged = false,
23426         dir, day, month,
23427         newDate, newViewDate;
23428         
23429         switch(e.keyCode){
23430             case 27: // escape
23431                 this.hidePopup();
23432                 e.preventDefault();
23433                 break;
23434             case 37: // left
23435             case 39: // right
23436                 if (!this.keyboardNavigation) {
23437                     break;
23438                 }
23439                 dir = e.keyCode == 37 ? -1 : 1;
23440                 
23441                 if (e.ctrlKey){
23442                     newDate = this.moveYear(this.date, dir);
23443                     newViewDate = this.moveYear(this.viewDate, dir);
23444                 } else if (e.shiftKey){
23445                     newDate = this.moveMonth(this.date, dir);
23446                     newViewDate = this.moveMonth(this.viewDate, dir);
23447                 } else {
23448                     newDate = new Date(this.date);
23449                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23450                     newViewDate = new Date(this.viewDate);
23451                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23452                 }
23453                 if (this.dateWithinRange(newDate)){
23454                     this.date = newDate;
23455                     this.viewDate = newViewDate;
23456                     this.setValue(this.formatDate(this.date));
23457 //                    this.update();
23458                     e.preventDefault();
23459                     dateChanged = true;
23460                 }
23461                 break;
23462             case 38: // up
23463             case 40: // down
23464                 if (!this.keyboardNavigation) {
23465                     break;
23466                 }
23467                 dir = e.keyCode == 38 ? -1 : 1;
23468                 if (e.ctrlKey){
23469                     newDate = this.moveYear(this.date, dir);
23470                     newViewDate = this.moveYear(this.viewDate, dir);
23471                 } else if (e.shiftKey){
23472                     newDate = this.moveMonth(this.date, dir);
23473                     newViewDate = this.moveMonth(this.viewDate, dir);
23474                 } else {
23475                     newDate = new Date(this.date);
23476                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23477                     newViewDate = new Date(this.viewDate);
23478                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23479                 }
23480                 if (this.dateWithinRange(newDate)){
23481                     this.date = newDate;
23482                     this.viewDate = newViewDate;
23483                     this.setValue(this.formatDate(this.date));
23484 //                    this.update();
23485                     e.preventDefault();
23486                     dateChanged = true;
23487                 }
23488                 break;
23489             case 13: // enter
23490                 this.setValue(this.formatDate(this.date));
23491                 this.hidePopup();
23492                 e.preventDefault();
23493                 break;
23494             case 9: // tab
23495                 this.setValue(this.formatDate(this.date));
23496                 this.hidePopup();
23497                 break;
23498             case 16: // shift
23499             case 17: // ctrl
23500             case 18: // alt
23501                 break;
23502             default :
23503                 this.hidePopup();
23504                 
23505         }
23506     },
23507     
23508     
23509     onClick: function(e) 
23510     {
23511         e.stopPropagation();
23512         e.preventDefault();
23513         
23514         var target = e.getTarget();
23515         
23516         if(target.nodeName.toLowerCase() === 'i'){
23517             target = Roo.get(target).dom.parentNode;
23518         }
23519         
23520         var nodeName = target.nodeName;
23521         var className = target.className;
23522         var html = target.innerHTML;
23523         //Roo.log(nodeName);
23524         
23525         switch(nodeName.toLowerCase()) {
23526             case 'th':
23527                 switch(className) {
23528                     case 'switch':
23529                         this.showMode(1);
23530                         break;
23531                     case 'prev':
23532                     case 'next':
23533                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23534                         switch(this.viewMode){
23535                                 case 0:
23536                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23537                                         break;
23538                                 case 1:
23539                                 case 2:
23540                                         this.viewDate = this.moveYear(this.viewDate, dir);
23541                                         break;
23542                         }
23543                         this.fill();
23544                         break;
23545                     case 'today':
23546                         var date = new Date();
23547                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23548 //                        this.fill()
23549                         this.setValue(this.formatDate(this.date));
23550                         
23551                         this.hidePopup();
23552                         break;
23553                 }
23554                 break;
23555             case 'span':
23556                 if (className.indexOf('disabled') < 0) {
23557                 if (!this.viewDate) {
23558                     this.viewDate = new Date();
23559                 }
23560                 this.viewDate.setUTCDate(1);
23561                     if (className.indexOf('month') > -1) {
23562                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23563                     } else {
23564                         var year = parseInt(html, 10) || 0;
23565                         this.viewDate.setUTCFullYear(year);
23566                         
23567                     }
23568                     
23569                     if(this.singleMode){
23570                         this.setValue(this.formatDate(this.viewDate));
23571                         this.hidePopup();
23572                         return;
23573                     }
23574                     
23575                     this.showMode(-1);
23576                     this.fill();
23577                 }
23578                 break;
23579                 
23580             case 'td':
23581                 //Roo.log(className);
23582                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23583                     var day = parseInt(html, 10) || 1;
23584                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23585                         month = (this.viewDate || new Date()).getUTCMonth();
23586
23587                     if (className.indexOf('old') > -1) {
23588                         if(month === 0 ){
23589                             month = 11;
23590                             year -= 1;
23591                         }else{
23592                             month -= 1;
23593                         }
23594                     } else if (className.indexOf('new') > -1) {
23595                         if (month == 11) {
23596                             month = 0;
23597                             year += 1;
23598                         } else {
23599                             month += 1;
23600                         }
23601                     }
23602                     //Roo.log([year,month,day]);
23603                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23604                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23605 //                    this.fill();
23606                     //Roo.log(this.formatDate(this.date));
23607                     this.setValue(this.formatDate(this.date));
23608                     this.hidePopup();
23609                 }
23610                 break;
23611         }
23612     },
23613     
23614     setStartDate: function(startDate)
23615     {
23616         this.startDate = startDate || -Infinity;
23617         if (this.startDate !== -Infinity) {
23618             this.startDate = this.parseDate(this.startDate);
23619         }
23620         this.update();
23621         this.updateNavArrows();
23622     },
23623
23624     setEndDate: function(endDate)
23625     {
23626         this.endDate = endDate || Infinity;
23627         if (this.endDate !== Infinity) {
23628             this.endDate = this.parseDate(this.endDate);
23629         }
23630         this.update();
23631         this.updateNavArrows();
23632     },
23633     
23634     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23635     {
23636         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23637         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23638             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23639         }
23640         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23641             return parseInt(d, 10);
23642         });
23643         this.update();
23644         this.updateNavArrows();
23645     },
23646     
23647     updateNavArrows: function() 
23648     {
23649         if(this.singleMode){
23650             return;
23651         }
23652         
23653         var d = new Date(this.viewDate),
23654         year = d.getUTCFullYear(),
23655         month = d.getUTCMonth();
23656         
23657         Roo.each(this.picker().select('.prev', true).elements, function(v){
23658             v.show();
23659             switch (this.viewMode) {
23660                 case 0:
23661
23662                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23663                         v.hide();
23664                     }
23665                     break;
23666                 case 1:
23667                 case 2:
23668                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23669                         v.hide();
23670                     }
23671                     break;
23672             }
23673         });
23674         
23675         Roo.each(this.picker().select('.next', true).elements, function(v){
23676             v.show();
23677             switch (this.viewMode) {
23678                 case 0:
23679
23680                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23681                         v.hide();
23682                     }
23683                     break;
23684                 case 1:
23685                 case 2:
23686                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23687                         v.hide();
23688                     }
23689                     break;
23690             }
23691         })
23692     },
23693     
23694     moveMonth: function(date, dir)
23695     {
23696         if (!dir) {
23697             return date;
23698         }
23699         var new_date = new Date(date.valueOf()),
23700         day = new_date.getUTCDate(),
23701         month = new_date.getUTCMonth(),
23702         mag = Math.abs(dir),
23703         new_month, test;
23704         dir = dir > 0 ? 1 : -1;
23705         if (mag == 1){
23706             test = dir == -1
23707             // If going back one month, make sure month is not current month
23708             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23709             ? function(){
23710                 return new_date.getUTCMonth() == month;
23711             }
23712             // If going forward one month, make sure month is as expected
23713             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23714             : function(){
23715                 return new_date.getUTCMonth() != new_month;
23716             };
23717             new_month = month + dir;
23718             new_date.setUTCMonth(new_month);
23719             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23720             if (new_month < 0 || new_month > 11) {
23721                 new_month = (new_month + 12) % 12;
23722             }
23723         } else {
23724             // For magnitudes >1, move one month at a time...
23725             for (var i=0; i<mag; i++) {
23726                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23727                 new_date = this.moveMonth(new_date, dir);
23728             }
23729             // ...then reset the day, keeping it in the new month
23730             new_month = new_date.getUTCMonth();
23731             new_date.setUTCDate(day);
23732             test = function(){
23733                 return new_month != new_date.getUTCMonth();
23734             };
23735         }
23736         // Common date-resetting loop -- if date is beyond end of month, make it
23737         // end of month
23738         while (test()){
23739             new_date.setUTCDate(--day);
23740             new_date.setUTCMonth(new_month);
23741         }
23742         return new_date;
23743     },
23744
23745     moveYear: function(date, dir)
23746     {
23747         return this.moveMonth(date, dir*12);
23748     },
23749
23750     dateWithinRange: function(date)
23751     {
23752         return date >= this.startDate && date <= this.endDate;
23753     },
23754
23755     
23756     remove: function() 
23757     {
23758         this.picker().remove();
23759     },
23760     
23761     validateValue : function(value)
23762     {
23763         if(this.getVisibilityEl().hasClass('hidden')){
23764             return true;
23765         }
23766         
23767         if(value.length < 1)  {
23768             if(this.allowBlank){
23769                 return true;
23770             }
23771             return false;
23772         }
23773         
23774         if(value.length < this.minLength){
23775             return false;
23776         }
23777         if(value.length > this.maxLength){
23778             return false;
23779         }
23780         if(this.vtype){
23781             var vt = Roo.form.VTypes;
23782             if(!vt[this.vtype](value, this)){
23783                 return false;
23784             }
23785         }
23786         if(typeof this.validator == "function"){
23787             var msg = this.validator(value);
23788             if(msg !== true){
23789                 return false;
23790             }
23791         }
23792         
23793         if(this.regex && !this.regex.test(value)){
23794             return false;
23795         }
23796         
23797         if(typeof(this.parseDate(value)) == 'undefined'){
23798             return false;
23799         }
23800         
23801         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23802             return false;
23803         }      
23804         
23805         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23806             return false;
23807         } 
23808         
23809         
23810         return true;
23811     },
23812     
23813     reset : function()
23814     {
23815         this.date = this.viewDate = '';
23816         
23817         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23818     }
23819    
23820 });
23821
23822 Roo.apply(Roo.bootstrap.form.DateField,  {
23823     
23824     head : {
23825         tag: 'thead',
23826         cn: [
23827         {
23828             tag: 'tr',
23829             cn: [
23830             {
23831                 tag: 'th',
23832                 cls: 'prev',
23833                 html: '<i class="fa fa-arrow-left"/>'
23834             },
23835             {
23836                 tag: 'th',
23837                 cls: 'switch',
23838                 colspan: '5'
23839             },
23840             {
23841                 tag: 'th',
23842                 cls: 'next',
23843                 html: '<i class="fa fa-arrow-right"/>'
23844             }
23845
23846             ]
23847         }
23848         ]
23849     },
23850     
23851     content : {
23852         tag: 'tbody',
23853         cn: [
23854         {
23855             tag: 'tr',
23856             cn: [
23857             {
23858                 tag: 'td',
23859                 colspan: '7'
23860             }
23861             ]
23862         }
23863         ]
23864     },
23865     
23866     footer : {
23867         tag: 'tfoot',
23868         cn: [
23869         {
23870             tag: 'tr',
23871             cn: [
23872             {
23873                 tag: 'th',
23874                 colspan: '7',
23875                 cls: 'today'
23876             }
23877                     
23878             ]
23879         }
23880         ]
23881     },
23882     
23883     dates:{
23884         en: {
23885             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23886             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23887             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23888             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23889             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23890             today: "Today"
23891         }
23892     },
23893     
23894     modes: [
23895     {
23896         clsName: 'days',
23897         navFnc: 'Month',
23898         navStep: 1
23899     },
23900     {
23901         clsName: 'months',
23902         navFnc: 'FullYear',
23903         navStep: 1
23904     },
23905     {
23906         clsName: 'years',
23907         navFnc: 'FullYear',
23908         navStep: 10
23909     }]
23910 });
23911
23912 Roo.apply(Roo.bootstrap.form.DateField,  {
23913   
23914     template : {
23915         tag: 'div',
23916         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23917         cn: [
23918         {
23919             tag: 'div',
23920             cls: 'datepicker-days',
23921             cn: [
23922             {
23923                 tag: 'table',
23924                 cls: 'table-condensed',
23925                 cn:[
23926                 Roo.bootstrap.form.DateField.head,
23927                 {
23928                     tag: 'tbody'
23929                 },
23930                 Roo.bootstrap.form.DateField.footer
23931                 ]
23932             }
23933             ]
23934         },
23935         {
23936             tag: 'div',
23937             cls: 'datepicker-months',
23938             cn: [
23939             {
23940                 tag: 'table',
23941                 cls: 'table-condensed',
23942                 cn:[
23943                 Roo.bootstrap.form.DateField.head,
23944                 Roo.bootstrap.form.DateField.content,
23945                 Roo.bootstrap.form.DateField.footer
23946                 ]
23947             }
23948             ]
23949         },
23950         {
23951             tag: 'div',
23952             cls: 'datepicker-years',
23953             cn: [
23954             {
23955                 tag: 'table',
23956                 cls: 'table-condensed',
23957                 cn:[
23958                 Roo.bootstrap.form.DateField.head,
23959                 Roo.bootstrap.form.DateField.content,
23960                 Roo.bootstrap.form.DateField.footer
23961                 ]
23962             }
23963             ]
23964         }
23965         ]
23966     }
23967 });
23968
23969  
23970
23971  /*
23972  * - LGPL
23973  *
23974  * TimeField
23975  * 
23976  */
23977
23978 /**
23979  * @class Roo.bootstrap.form.TimeField
23980  * @extends Roo.bootstrap.form.Input
23981  * Bootstrap DateField class
23982  * @cfg {Number} minuteStep the minutes goes up/down by a fixed number, default 1
23983  * 
23984  * 
23985  * @constructor
23986  * Create a new TimeField
23987  * @param {Object} config The config object
23988  */
23989
23990 Roo.bootstrap.form.TimeField = function(config){
23991     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23992     this.addEvents({
23993             /**
23994              * @event show
23995              * Fires when this field show.
23996              * @param {Roo.bootstrap.form.DateField} thisthis
23997              * @param {Mixed} date The date value
23998              */
23999             show : true,
24000             /**
24001              * @event show
24002              * Fires when this field hide.
24003              * @param {Roo.bootstrap.form.DateField} this
24004              * @param {Mixed} date The date value
24005              */
24006             hide : true,
24007             /**
24008              * @event select
24009              * Fires when select a date.
24010              * @param {Roo.bootstrap.form.DateField} this
24011              * @param {Mixed} date The date value
24012              */
24013             select : true
24014         });
24015 };
24016
24017 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
24018     
24019     /**
24020      * @cfg {String} format
24021      * The default time format string which can be overriden for localization support.  The format must be
24022      * valid according to {@link Date#parseDate} (defaults to 'H:i').
24023      */
24024     format : "H:i",
24025     minuteStep : 1,
24026
24027     getAutoCreate : function()
24028     {
24029         this.after = '<i class="fa far fa-clock"></i>';
24030         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
24031         
24032          
24033     },
24034     onRender: function(ct, position)
24035     {
24036         
24037         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24038                 
24039         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24040         
24041         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24042         
24043         this.pop = this.picker().select('>.datepicker-time',true).first();
24044         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24045         
24046         this.picker().on('mousedown', this.onMousedown, this);
24047         this.picker().on('click', this.onClick, this);
24048         
24049         this.picker().addClass('datepicker-dropdown');
24050     
24051         this.fillTime();
24052         this.update();
24053             
24054         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24055         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24056         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24057         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24058         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24059         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24060
24061     },
24062     
24063     fireKey: function(e){
24064         if (!this.picker().isVisible()){
24065             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24066                 this.show();
24067             }
24068             return;
24069         }
24070
24071         e.preventDefault();
24072         
24073         switch(e.keyCode){
24074             case 27: // escape
24075                 this.hide();
24076                 break;
24077             case 37: // left
24078             case 39: // right
24079                 this.onTogglePeriod();
24080                 break;
24081             case 38: // up
24082                 this.onIncrementMinutes();
24083                 break;
24084             case 40: // down
24085                 this.onDecrementMinutes();
24086                 break;
24087             case 13: // enter
24088             case 9: // tab
24089                 this.setTime();
24090                 break;
24091         }
24092     },
24093     
24094     onClick: function(e) {
24095         e.stopPropagation();
24096         e.preventDefault();
24097     },
24098     
24099     picker : function()
24100     {
24101         return this.pickerEl;
24102     },
24103     
24104     fillTime: function()
24105     {    
24106         var time = this.pop.select('tbody', true).first();
24107         
24108         time.dom.innerHTML = '';
24109         
24110         time.createChild({
24111             tag: 'tr',
24112             cn: [
24113                 {
24114                     tag: 'td',
24115                     cn: [
24116                         {
24117                             tag: 'a',
24118                             href: '#',
24119                             cls: 'btn',
24120                             cn: [
24121                                 {
24122                                     tag: 'i',
24123                                     cls: 'hours-up fa fas fa-chevron-up'
24124                                 }
24125                             ]
24126                         } 
24127                     ]
24128                 },
24129                 {
24130                     tag: 'td',
24131                     cls: 'separator'
24132                 },
24133                 {
24134                     tag: 'td',
24135                     cn: [
24136                         {
24137                             tag: 'a',
24138                             href: '#',
24139                             cls: 'btn',
24140                             cn: [
24141                                 {
24142                                     tag: 'i',
24143                                     cls: 'minutes-up fa fas fa-chevron-up'
24144                                 }
24145                             ]
24146                         }
24147                     ]
24148                 },
24149                 {
24150                     tag: 'td',
24151                     cls: 'separator'
24152                 }
24153             ]
24154         });
24155         
24156         time.createChild({
24157             tag: 'tr',
24158             cn: [
24159                 {
24160                     tag: 'td',
24161                     cn: [
24162                         {
24163                             tag: 'span',
24164                             cls: 'timepicker-hour',
24165                             html: '00'
24166                         }  
24167                     ]
24168                 },
24169                 {
24170                     tag: 'td',
24171                     cls: 'separator',
24172                     html: ':'
24173                 },
24174                 {
24175                     tag: 'td',
24176                     cn: [
24177                         {
24178                             tag: 'span',
24179                             cls: 'timepicker-minute',
24180                             html: '00'
24181                         }  
24182                     ]
24183                 },
24184                 {
24185                     tag: 'td',
24186                     cls: 'separator'
24187                 },
24188                 {
24189                     tag: 'td',
24190                     cn: [
24191                         {
24192                             tag: 'button',
24193                             type: 'button',
24194                             cls: 'btn btn-primary period',
24195                             html: 'AM'
24196                             
24197                         }
24198                     ]
24199                 }
24200             ]
24201         });
24202         
24203         time.createChild({
24204             tag: 'tr',
24205             cn: [
24206                 {
24207                     tag: 'td',
24208                     cn: [
24209                         {
24210                             tag: 'a',
24211                             href: '#',
24212                             cls: 'btn',
24213                             cn: [
24214                                 {
24215                                     tag: 'span',
24216                                     cls: 'hours-down fa fas fa-chevron-down'
24217                                 }
24218                             ]
24219                         }
24220                     ]
24221                 },
24222                 {
24223                     tag: 'td',
24224                     cls: 'separator'
24225                 },
24226                 {
24227                     tag: 'td',
24228                     cn: [
24229                         {
24230                             tag: 'a',
24231                             href: '#',
24232                             cls: 'btn',
24233                             cn: [
24234                                 {
24235                                     tag: 'span',
24236                                     cls: 'minutes-down fa fas fa-chevron-down'
24237                                 }
24238                             ]
24239                         }
24240                     ]
24241                 },
24242                 {
24243                     tag: 'td',
24244                     cls: 'separator'
24245                 }
24246             ]
24247         });
24248         
24249     },
24250     
24251     update: function()
24252     {
24253         
24254         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24255         
24256         this.fill();
24257     },
24258     
24259     fill: function() 
24260     {
24261         var hours = this.time.getHours();
24262         var minutes = this.time.getMinutes();
24263         var period = 'AM';
24264         
24265         if(hours > 11){
24266             period = 'PM';
24267         }
24268         
24269         if(hours == 0){
24270             hours = 12;
24271         }
24272         
24273         
24274         if(hours > 12){
24275             hours = hours - 12;
24276         }
24277         
24278         if(hours < 10){
24279             hours = '0' + hours;
24280         }
24281         
24282         if(minutes < 10){
24283             minutes = '0' + minutes;
24284         }
24285         
24286         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24287         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24288         this.pop.select('button', true).first().dom.innerHTML = period;
24289         
24290     },
24291     
24292     place: function()
24293     {   
24294         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24295         
24296         var cls = ['bottom'];
24297         
24298         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24299             cls.pop();
24300             cls.push('top');
24301         }
24302         
24303         cls.push('right');
24304         
24305         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24306             cls.pop();
24307             cls.push('left');
24308         }
24309         //this.picker().setXY(20000,20000);
24310         this.picker().addClass(cls.join('-'));
24311         
24312         var _this = this;
24313         
24314         Roo.each(cls, function(c){
24315             if(c == 'bottom'){
24316                 (function() {
24317                  //  
24318                 }).defer(200);
24319                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24320                 //_this.picker().setTop(_this.inputEl().getHeight());
24321                 return;
24322             }
24323             if(c == 'top'){
24324                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24325                 
24326                 //_this.picker().setTop(0 - _this.picker().getHeight());
24327                 return;
24328             }
24329             /*
24330             if(c == 'left'){
24331                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24332                 return;
24333             }
24334             if(c == 'right'){
24335                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24336                 return;
24337             }
24338             */
24339         });
24340         
24341     },
24342   
24343     onFocus : function()
24344     {
24345         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24346         this.show();
24347     },
24348     
24349     onBlur : function()
24350     {
24351         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24352         this.hide();
24353     },
24354     
24355     show : function()
24356     {
24357         this.picker().show();
24358         this.pop.show();
24359         this.update();
24360         this.place();
24361         
24362         this.fireEvent('show', this, this.date);
24363     },
24364     
24365     hide : function()
24366     {
24367         this.picker().hide();
24368         this.pop.hide();
24369         
24370         this.fireEvent('hide', this, this.date);
24371     },
24372     
24373     setTime : function()
24374     {
24375         this.hide();
24376         this.setValue(this.time.format(this.format));
24377         
24378         this.fireEvent('select', this, this.date);
24379         
24380         
24381     },
24382     
24383     onMousedown: function(e){
24384         e.stopPropagation();
24385         e.preventDefault();
24386     },
24387     
24388     onIncrementHours: function()
24389     {
24390         Roo.log('onIncrementHours');
24391         this.time = this.time.add(Date.HOUR, 1);
24392         this.update();
24393         
24394     },
24395     
24396     onDecrementHours: function()
24397     {
24398         Roo.log('onDecrementHours');
24399         this.time = this.time.add(Date.HOUR, -1);
24400         this.update();
24401     },
24402     
24403     onIncrementMinutes: function()
24404     {
24405         Roo.log('onIncrementMinutes');
24406         var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24407         this.time = this.time.add(Date.MINUTE, minutesToAdd);
24408         this.update();
24409     },
24410     
24411     onDecrementMinutes: function()
24412     {
24413         Roo.log('onDecrementMinutes');
24414         var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24415         this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24416         this.update();
24417     },
24418     
24419     onTogglePeriod: function()
24420     {
24421         Roo.log('onTogglePeriod');
24422         this.time = this.time.add(Date.HOUR, 12);
24423         this.update();
24424     }
24425     
24426    
24427 });
24428  
24429
24430 Roo.apply(Roo.bootstrap.form.TimeField,  {
24431   
24432     template : {
24433         tag: 'div',
24434         cls: 'datepicker dropdown-menu',
24435         cn: [
24436             {
24437                 tag: 'div',
24438                 cls: 'datepicker-time',
24439                 cn: [
24440                 {
24441                     tag: 'table',
24442                     cls: 'table-condensed',
24443                     cn:[
24444                         {
24445                             tag: 'tbody',
24446                             cn: [
24447                                 {
24448                                     tag: 'tr',
24449                                     cn: [
24450                                     {
24451                                         tag: 'td',
24452                                         colspan: '7'
24453                                     }
24454                                     ]
24455                                 }
24456                             ]
24457                         },
24458                         {
24459                             tag: 'tfoot',
24460                             cn: [
24461                                 {
24462                                     tag: 'tr',
24463                                     cn: [
24464                                     {
24465                                         tag: 'th',
24466                                         colspan: '7',
24467                                         cls: '',
24468                                         cn: [
24469                                             {
24470                                                 tag: 'button',
24471                                                 cls: 'btn btn-info ok',
24472                                                 html: 'OK'
24473                                             }
24474                                         ]
24475                                     }
24476                     
24477                                     ]
24478                                 }
24479                             ]
24480                         }
24481                     ]
24482                 }
24483                 ]
24484             }
24485         ]
24486     }
24487 });
24488
24489  
24490
24491  /*
24492  * - LGPL
24493  *
24494  * MonthField
24495  * 
24496  */
24497
24498 /**
24499  * @class Roo.bootstrap.form.MonthField
24500  * @extends Roo.bootstrap.form.Input
24501  * Bootstrap MonthField class
24502  * 
24503  * @cfg {String} language default en
24504  * 
24505  * @constructor
24506  * Create a new MonthField
24507  * @param {Object} config The config object
24508  */
24509
24510 Roo.bootstrap.form.MonthField = function(config){
24511     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24512     
24513     this.addEvents({
24514         /**
24515          * @event show
24516          * Fires when this field show.
24517          * @param {Roo.bootstrap.form.MonthField} this
24518          * @param {Mixed} date The date value
24519          */
24520         show : true,
24521         /**
24522          * @event show
24523          * Fires when this field hide.
24524          * @param {Roo.bootstrap.form.MonthField} this
24525          * @param {Mixed} date The date value
24526          */
24527         hide : true,
24528         /**
24529          * @event select
24530          * Fires when select a date.
24531          * @param {Roo.bootstrap.form.MonthField} this
24532          * @param {String} oldvalue The old value
24533          * @param {String} newvalue The new value
24534          */
24535         select : true
24536     });
24537 };
24538
24539 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24540     
24541     onRender: function(ct, position)
24542     {
24543         
24544         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24545         
24546         this.language = this.language || 'en';
24547         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24548         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24549         
24550         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24551         this.isInline = false;
24552         this.isInput = true;
24553         this.component = this.el.select('.add-on', true).first() || false;
24554         this.component = (this.component && this.component.length === 0) ? false : this.component;
24555         this.hasInput = this.component && this.inputEL().length;
24556         
24557         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24558         
24559         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24560         
24561         this.picker().on('mousedown', this.onMousedown, this);
24562         this.picker().on('click', this.onClick, this);
24563         
24564         this.picker().addClass('datepicker-dropdown');
24565         
24566         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24567             v.setStyle('width', '189px');
24568         });
24569         
24570         this.fillMonths();
24571         
24572         this.update();
24573         
24574         if(this.isInline) {
24575             this.show();
24576         }
24577         
24578     },
24579     
24580     setValue: function(v, suppressEvent)
24581     {   
24582         var o = this.getValue();
24583         
24584         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24585         
24586         this.update();
24587
24588         if(suppressEvent !== true){
24589             this.fireEvent('select', this, o, v);
24590         }
24591         
24592     },
24593     
24594     getValue: function()
24595     {
24596         return this.value;
24597     },
24598     
24599     onClick: function(e) 
24600     {
24601         e.stopPropagation();
24602         e.preventDefault();
24603         
24604         var target = e.getTarget();
24605         
24606         if(target.nodeName.toLowerCase() === 'i'){
24607             target = Roo.get(target).dom.parentNode;
24608         }
24609         
24610         var nodeName = target.nodeName;
24611         var className = target.className;
24612         var html = target.innerHTML;
24613         
24614         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24615             return;
24616         }
24617         
24618         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24619         
24620         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24621         
24622         this.hide();
24623                         
24624     },
24625     
24626     picker : function()
24627     {
24628         return this.pickerEl;
24629     },
24630     
24631     fillMonths: function()
24632     {    
24633         var i = 0;
24634         var months = this.picker().select('>.datepicker-months td', true).first();
24635         
24636         months.dom.innerHTML = '';
24637         
24638         while (i < 12) {
24639             var month = {
24640                 tag: 'span',
24641                 cls: 'month',
24642                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24643             };
24644             
24645             months.createChild(month);
24646         }
24647         
24648     },
24649     
24650     update: function()
24651     {
24652         var _this = this;
24653         
24654         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24655             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24656         }
24657         
24658         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24659             e.removeClass('active');
24660             
24661             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24662                 e.addClass('active');
24663             }
24664         })
24665     },
24666     
24667     place: function()
24668     {
24669         if(this.isInline) {
24670             return;
24671         }
24672         
24673         this.picker().removeClass(['bottom', 'top']);
24674         
24675         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24676             /*
24677              * place to the top of element!
24678              *
24679              */
24680             
24681             this.picker().addClass('top');
24682             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24683             
24684             return;
24685         }
24686         
24687         this.picker().addClass('bottom');
24688         
24689         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24690     },
24691     
24692     onFocus : function()
24693     {
24694         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24695         this.show();
24696     },
24697     
24698     onBlur : function()
24699     {
24700         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24701         
24702         var d = this.inputEl().getValue();
24703         
24704         this.setValue(d);
24705                 
24706         this.hide();
24707     },
24708     
24709     show : function()
24710     {
24711         this.picker().show();
24712         this.picker().select('>.datepicker-months', true).first().show();
24713         this.update();
24714         this.place();
24715         
24716         this.fireEvent('show', this, this.date);
24717     },
24718     
24719     hide : function()
24720     {
24721         if(this.isInline) {
24722             return;
24723         }
24724         this.picker().hide();
24725         this.fireEvent('hide', this, this.date);
24726         
24727     },
24728     
24729     onMousedown: function(e)
24730     {
24731         e.stopPropagation();
24732         e.preventDefault();
24733     },
24734     
24735     keyup: function(e)
24736     {
24737         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24738         this.update();
24739     },
24740
24741     fireKey: function(e)
24742     {
24743         if (!this.picker().isVisible()){
24744             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24745                 this.show();
24746             }
24747             return;
24748         }
24749         
24750         var dir;
24751         
24752         switch(e.keyCode){
24753             case 27: // escape
24754                 this.hide();
24755                 e.preventDefault();
24756                 break;
24757             case 37: // left
24758             case 39: // right
24759                 dir = e.keyCode == 37 ? -1 : 1;
24760                 
24761                 this.vIndex = this.vIndex + dir;
24762                 
24763                 if(this.vIndex < 0){
24764                     this.vIndex = 0;
24765                 }
24766                 
24767                 if(this.vIndex > 11){
24768                     this.vIndex = 11;
24769                 }
24770                 
24771                 if(isNaN(this.vIndex)){
24772                     this.vIndex = 0;
24773                 }
24774                 
24775                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24776                 
24777                 break;
24778             case 38: // up
24779             case 40: // down
24780                 
24781                 dir = e.keyCode == 38 ? -1 : 1;
24782                 
24783                 this.vIndex = this.vIndex + dir * 4;
24784                 
24785                 if(this.vIndex < 0){
24786                     this.vIndex = 0;
24787                 }
24788                 
24789                 if(this.vIndex > 11){
24790                     this.vIndex = 11;
24791                 }
24792                 
24793                 if(isNaN(this.vIndex)){
24794                     this.vIndex = 0;
24795                 }
24796                 
24797                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24798                 break;
24799                 
24800             case 13: // enter
24801                 
24802                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24803                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24804                 }
24805                 
24806                 this.hide();
24807                 e.preventDefault();
24808                 break;
24809             case 9: // tab
24810                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24811                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24812                 }
24813                 this.hide();
24814                 break;
24815             case 16: // shift
24816             case 17: // ctrl
24817             case 18: // alt
24818                 break;
24819             default :
24820                 this.hide();
24821                 
24822         }
24823     },
24824     
24825     remove: function() 
24826     {
24827         this.picker().remove();
24828     }
24829    
24830 });
24831
24832 Roo.apply(Roo.bootstrap.form.MonthField,  {
24833     
24834     content : {
24835         tag: 'tbody',
24836         cn: [
24837         {
24838             tag: 'tr',
24839             cn: [
24840             {
24841                 tag: 'td',
24842                 colspan: '7'
24843             }
24844             ]
24845         }
24846         ]
24847     },
24848     
24849     dates:{
24850         en: {
24851             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24852             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24853         }
24854     }
24855 });
24856
24857 Roo.apply(Roo.bootstrap.form.MonthField,  {
24858   
24859     template : {
24860         tag: 'div',
24861         cls: 'datepicker dropdown-menu roo-dynamic',
24862         cn: [
24863             {
24864                 tag: 'div',
24865                 cls: 'datepicker-months',
24866                 cn: [
24867                 {
24868                     tag: 'table',
24869                     cls: 'table-condensed',
24870                     cn:[
24871                         Roo.bootstrap.form.DateField.content
24872                     ]
24873                 }
24874                 ]
24875             }
24876         ]
24877     }
24878 });
24879
24880  
24881
24882  
24883  /*
24884  * - LGPL
24885  *
24886  * CheckBox
24887  * 
24888  */
24889
24890 /**
24891  * @class Roo.bootstrap.form.CheckBox
24892  * @extends Roo.bootstrap.form.Input
24893  * Bootstrap CheckBox class
24894  * 
24895  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24896  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24897  * @cfg {String} boxLabel The text that appears beside the checkbox
24898  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24899  * @cfg {Boolean} checked initnal the element
24900  * @cfg {Boolean} inline inline the element (default false)
24901  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24902  * @cfg {String} tooltip label tooltip
24903  * 
24904  * @constructor
24905  * Create a new CheckBox
24906  * @param {Object} config The config object
24907  */
24908
24909 Roo.bootstrap.form.CheckBox = function(config){
24910     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24911    
24912     this.addEvents({
24913         /**
24914         * @event check
24915         * Fires when the element is checked or unchecked.
24916         * @param {Roo.bootstrap.form.CheckBox} this This input
24917         * @param {Boolean} checked The new checked value
24918         */
24919        check : true,
24920        /**
24921         * @event click
24922         * Fires when the element is click.
24923         * @param {Roo.bootstrap.form.CheckBox} this This input
24924         */
24925        click : true
24926     });
24927     
24928 };
24929
24930 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24931   
24932     inputType: 'checkbox',
24933     inputValue: 1,
24934     valueOff: 0,
24935     boxLabel: false,
24936     checked: false,
24937     weight : false,
24938     inline: false,
24939     tooltip : '',
24940     
24941     // checkbox success does not make any sense really.. 
24942     invalidClass : "",
24943     validClass : "",
24944     
24945     
24946     getAutoCreate : function()
24947     {
24948         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24949         
24950         var id = Roo.id();
24951         
24952         var cfg = {};
24953         
24954         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24955         
24956         if(this.inline){
24957             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24958         }
24959         
24960         var input =  {
24961             tag: 'input',
24962             id : id,
24963             type : this.inputType,
24964             value : this.inputValue,
24965             cls : 'roo-' + this.inputType, //'form-box',
24966             placeholder : this.placeholder || ''
24967             
24968         };
24969         
24970         if(this.inputType != 'radio'){
24971             var hidden =  {
24972                 tag: 'input',
24973                 type : 'hidden',
24974                 cls : 'roo-hidden-value',
24975                 value : this.checked ? this.inputValue : this.valueOff
24976             };
24977         }
24978         
24979             
24980         if (this.weight) { // Validity check?
24981             cfg.cls += " " + this.inputType + "-" + this.weight;
24982         }
24983         
24984         if (this.disabled) {
24985             input.disabled=true;
24986         }
24987         
24988         if(this.checked){
24989             input.checked = this.checked;
24990         }
24991         
24992         if (this.name) {
24993             
24994             input.name = this.name;
24995             
24996             if(this.inputType != 'radio'){
24997                 hidden.name = this.name;
24998                 input.name = '_hidden_' + this.name;
24999             }
25000         }
25001         
25002         if (this.size) {
25003             input.cls += ' input-' + this.size;
25004         }
25005         
25006         var settings=this;
25007         
25008         ['xs','sm','md','lg'].map(function(size){
25009             if (settings[size]) {
25010                 cfg.cls += ' col-' + size + '-' + settings[size];
25011             }
25012         });
25013         
25014         var inputblock = input;
25015          
25016         if (this.before || this.after) {
25017             
25018             inputblock = {
25019                 cls : 'input-group',
25020                 cn :  [] 
25021             };
25022             
25023             if (this.before) {
25024                 inputblock.cn.push({
25025                     tag :'span',
25026                     cls : 'input-group-addon',
25027                     html : this.before
25028                 });
25029             }
25030             
25031             inputblock.cn.push(input);
25032             
25033             if(this.inputType != 'radio'){
25034                 inputblock.cn.push(hidden);
25035             }
25036             
25037             if (this.after) {
25038                 inputblock.cn.push({
25039                     tag :'span',
25040                     cls : 'input-group-addon',
25041                     html : this.after
25042                 });
25043             }
25044             
25045         }
25046         var boxLabelCfg = false;
25047         
25048         if(this.boxLabel){
25049            
25050             boxLabelCfg = {
25051                 tag: 'label',
25052                 //'for': id, // box label is handled by onclick - so no for...
25053                 cls: 'box-label',
25054                 html: this.boxLabel
25055             };
25056             if(this.tooltip){
25057                 boxLabelCfg.tooltip = this.tooltip;
25058             }
25059              
25060         }
25061         
25062         
25063         if (align ==='left' && this.fieldLabel.length) {
25064 //                Roo.log("left and has label");
25065             cfg.cn = [
25066                 {
25067                     tag: 'label',
25068                     'for' :  id,
25069                     cls : 'control-label',
25070                     html : this.fieldLabel
25071                 },
25072                 {
25073                     cls : "", 
25074                     cn: [
25075                         inputblock
25076                     ]
25077                 }
25078             ];
25079             
25080             if (boxLabelCfg) {
25081                 cfg.cn[1].cn.push(boxLabelCfg);
25082             }
25083             
25084             if(this.labelWidth > 12){
25085                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25086             }
25087             
25088             if(this.labelWidth < 13 && this.labelmd == 0){
25089                 this.labelmd = this.labelWidth;
25090             }
25091             
25092             if(this.labellg > 0){
25093                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25094                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25095             }
25096             
25097             if(this.labelmd > 0){
25098                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25099                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25100             }
25101             
25102             if(this.labelsm > 0){
25103                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25104                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25105             }
25106             
25107             if(this.labelxs > 0){
25108                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25109                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25110             }
25111             
25112         } else if ( this.fieldLabel.length) {
25113 //                Roo.log(" label");
25114                 cfg.cn = [
25115                    
25116                     {
25117                         tag: this.boxLabel ? 'span' : 'label',
25118                         'for': id,
25119                         cls: 'control-label box-input-label',
25120                         //cls : 'input-group-addon',
25121                         html : this.fieldLabel
25122                     },
25123                     
25124                     inputblock
25125                     
25126                 ];
25127                 if (boxLabelCfg) {
25128                     cfg.cn.push(boxLabelCfg);
25129                 }
25130
25131         } else {
25132             
25133 //                Roo.log(" no label && no align");
25134                 cfg.cn = [  inputblock ] ;
25135                 if (boxLabelCfg) {
25136                     cfg.cn.push(boxLabelCfg);
25137                 }
25138
25139                 
25140         }
25141         
25142        
25143         
25144         if(this.inputType != 'radio'){
25145             cfg.cn.push(hidden);
25146         }
25147         
25148         return cfg;
25149         
25150     },
25151     
25152     /**
25153      * return the real input element.
25154      */
25155     inputEl: function ()
25156     {
25157         return this.el.select('input.roo-' + this.inputType,true).first();
25158     },
25159     hiddenEl: function ()
25160     {
25161         return this.el.select('input.roo-hidden-value',true).first();
25162     },
25163     
25164     labelEl: function()
25165     {
25166         return this.el.select('label.control-label',true).first();
25167     },
25168     /* depricated... */
25169     
25170     label: function()
25171     {
25172         return this.labelEl();
25173     },
25174     
25175     boxLabelEl: function()
25176     {
25177         return this.el.select('label.box-label',true).first();
25178     },
25179     
25180     initEvents : function()
25181     {
25182 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25183         
25184         this.inputEl().on('click', this.onClick,  this);
25185         
25186         if (this.boxLabel) { 
25187             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25188         }
25189         
25190         this.startValue = this.getValue();
25191         
25192         if(this.groupId){
25193             Roo.bootstrap.form.CheckBox.register(this);
25194         }
25195     },
25196     
25197     onClick : function(e)
25198     {   
25199         if(this.fireEvent('click', this, e) !== false){
25200             this.setChecked(!this.checked);
25201         }
25202         
25203     },
25204     
25205     setChecked : function(state,suppressEvent)
25206     {
25207         this.startValue = this.getValue();
25208
25209         if(this.inputType == 'radio'){
25210             
25211             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25212                 e.dom.checked = false;
25213             });
25214             
25215             this.inputEl().dom.checked = true;
25216             
25217             this.inputEl().dom.value = this.inputValue;
25218             
25219             if(suppressEvent !== true){
25220                 this.fireEvent('check', this, true);
25221             }
25222             
25223             this.validate();
25224             
25225             return;
25226         }
25227         
25228         this.checked = state;
25229         
25230         this.inputEl().dom.checked = state;
25231         
25232         
25233         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25234         
25235         if(suppressEvent !== true){
25236             this.fireEvent('check', this, state);
25237         }
25238         
25239         this.validate();
25240     },
25241     
25242     getValue : function()
25243     {
25244         if(this.inputType == 'radio'){
25245             return this.getGroupValue();
25246         }
25247         
25248         return this.hiddenEl().dom.value;
25249         
25250     },
25251     
25252     getGroupValue : function()
25253     {
25254         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25255             return '';
25256         }
25257         
25258         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25259     },
25260     
25261     setValue : function(v,suppressEvent)
25262     {
25263         if(this.inputType == 'radio'){
25264             this.setGroupValue(v, suppressEvent);
25265             return;
25266         }
25267         
25268         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25269         
25270         this.validate();
25271     },
25272     
25273     setGroupValue : function(v, suppressEvent)
25274     {
25275         this.startValue = this.getValue();
25276         
25277         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25278             e.dom.checked = false;
25279             
25280             if(e.dom.value == v){
25281                 e.dom.checked = true;
25282             }
25283         });
25284         
25285         if(suppressEvent !== true){
25286             this.fireEvent('check', this, true);
25287         }
25288
25289         this.validate();
25290         
25291         return;
25292     },
25293     
25294     validate : function()
25295     {
25296         if(this.getVisibilityEl().hasClass('hidden')){
25297             return true;
25298         }
25299         
25300         if(
25301                 this.disabled || 
25302                 (this.inputType == 'radio' && this.validateRadio()) ||
25303                 (this.inputType == 'checkbox' && this.validateCheckbox())
25304         ){
25305             this.markValid();
25306             return true;
25307         }
25308         
25309         this.markInvalid();
25310         return false;
25311     },
25312     
25313     validateRadio : function()
25314     {
25315         if(this.getVisibilityEl().hasClass('hidden')){
25316             return true;
25317         }
25318         
25319         if(this.allowBlank){
25320             return true;
25321         }
25322         
25323         var valid = false;
25324         
25325         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25326             if(!e.dom.checked){
25327                 return;
25328             }
25329             
25330             valid = true;
25331             
25332             return false;
25333         });
25334         
25335         return valid;
25336     },
25337     
25338     validateCheckbox : function()
25339     {
25340         if(!this.groupId){
25341             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25342             //return (this.getValue() == this.inputValue) ? true : false;
25343         }
25344         
25345         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25346         
25347         if(!group){
25348             return false;
25349         }
25350         
25351         var r = false;
25352         
25353         for(var i in group){
25354             if(group[i].el.isVisible(true)){
25355                 r = false;
25356                 break;
25357             }
25358             
25359             r = true;
25360         }
25361         
25362         for(var i in group){
25363             if(r){
25364                 break;
25365             }
25366             
25367             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25368         }
25369         
25370         return r;
25371     },
25372     
25373     /**
25374      * Mark this field as valid
25375      */
25376     markValid : function()
25377     {
25378         var _this = this;
25379         
25380         this.fireEvent('valid', this);
25381         
25382         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25383         
25384         if(this.groupId){
25385             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25386         }
25387         
25388         if(label){
25389             label.markValid();
25390         }
25391
25392         if(this.inputType == 'radio'){
25393             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25394                 var fg = e.findParent('.form-group', false, true);
25395                 if (Roo.bootstrap.version == 3) {
25396                     fg.removeClass([_this.invalidClass, _this.validClass]);
25397                     fg.addClass(_this.validClass);
25398                 } else {
25399                     fg.removeClass(['is-valid', 'is-invalid']);
25400                     fg.addClass('is-valid');
25401                 }
25402             });
25403             
25404             return;
25405         }
25406
25407         if(!this.groupId){
25408             var fg = this.el.findParent('.form-group', false, true);
25409             if (Roo.bootstrap.version == 3) {
25410                 fg.removeClass([this.invalidClass, this.validClass]);
25411                 fg.addClass(this.validClass);
25412             } else {
25413                 fg.removeClass(['is-valid', 'is-invalid']);
25414                 fg.addClass('is-valid');
25415             }
25416             return;
25417         }
25418         
25419         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25420         
25421         if(!group){
25422             return;
25423         }
25424         
25425         for(var i in group){
25426             var fg = group[i].el.findParent('.form-group', false, true);
25427             if (Roo.bootstrap.version == 3) {
25428                 fg.removeClass([this.invalidClass, this.validClass]);
25429                 fg.addClass(this.validClass);
25430             } else {
25431                 fg.removeClass(['is-valid', 'is-invalid']);
25432                 fg.addClass('is-valid');
25433             }
25434         }
25435     },
25436     
25437      /**
25438      * Mark this field as invalid
25439      * @param {String} msg The validation message
25440      */
25441     markInvalid : function(msg)
25442     {
25443         if(this.allowBlank){
25444             return;
25445         }
25446         
25447         var _this = this;
25448         
25449         this.fireEvent('invalid', this, msg);
25450         
25451         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25452         
25453         if(this.groupId){
25454             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25455         }
25456         
25457         if(label){
25458             label.markInvalid();
25459         }
25460             
25461         if(this.inputType == 'radio'){
25462             
25463             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25464                 var fg = e.findParent('.form-group', false, true);
25465                 if (Roo.bootstrap.version == 3) {
25466                     fg.removeClass([_this.invalidClass, _this.validClass]);
25467                     fg.addClass(_this.invalidClass);
25468                 } else {
25469                     fg.removeClass(['is-invalid', 'is-valid']);
25470                     fg.addClass('is-invalid');
25471                 }
25472             });
25473             
25474             return;
25475         }
25476         
25477         if(!this.groupId){
25478             var fg = this.el.findParent('.form-group', false, true);
25479             if (Roo.bootstrap.version == 3) {
25480                 fg.removeClass([_this.invalidClass, _this.validClass]);
25481                 fg.addClass(_this.invalidClass);
25482             } else {
25483                 fg.removeClass(['is-invalid', 'is-valid']);
25484                 fg.addClass('is-invalid');
25485             }
25486             return;
25487         }
25488         
25489         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25490         
25491         if(!group){
25492             return;
25493         }
25494         
25495         for(var i in group){
25496             var fg = group[i].el.findParent('.form-group', false, true);
25497             if (Roo.bootstrap.version == 3) {
25498                 fg.removeClass([_this.invalidClass, _this.validClass]);
25499                 fg.addClass(_this.invalidClass);
25500             } else {
25501                 fg.removeClass(['is-invalid', 'is-valid']);
25502                 fg.addClass('is-invalid');
25503             }
25504         }
25505         
25506     },
25507     
25508     clearInvalid : function()
25509     {
25510         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25511         
25512         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25513         
25514         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25515         
25516         if (label && label.iconEl) {
25517             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25518             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25519         }
25520     },
25521     
25522     disable : function()
25523     {
25524         if(this.inputType != 'radio'){
25525             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25526             return;
25527         }
25528         
25529         var _this = this;
25530         
25531         if(this.rendered){
25532             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25533                 _this.getActionEl().addClass(this.disabledClass);
25534                 e.dom.disabled = true;
25535             });
25536         }
25537         
25538         this.disabled = true;
25539         this.fireEvent("disable", this);
25540         return this;
25541     },
25542
25543     enable : function()
25544     {
25545         if(this.inputType != 'radio'){
25546             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25547             return;
25548         }
25549         
25550         var _this = this;
25551         
25552         if(this.rendered){
25553             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25554                 _this.getActionEl().removeClass(this.disabledClass);
25555                 e.dom.disabled = false;
25556             });
25557         }
25558         
25559         this.disabled = false;
25560         this.fireEvent("enable", this);
25561         return this;
25562     },
25563     
25564     setBoxLabel : function(v)
25565     {
25566         this.boxLabel = v;
25567         
25568         if(this.rendered){
25569             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25570         }
25571     }
25572
25573 });
25574
25575 Roo.apply(Roo.bootstrap.form.CheckBox, {
25576     
25577     groups: {},
25578     
25579      /**
25580     * register a CheckBox Group
25581     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25582     */
25583     register : function(checkbox)
25584     {
25585         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25586             this.groups[checkbox.groupId] = {};
25587         }
25588         
25589         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25590             return;
25591         }
25592         
25593         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25594         
25595     },
25596     /**
25597     * fetch a CheckBox Group based on the group ID
25598     * @param {string} the group ID
25599     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25600     */
25601     get: function(groupId) {
25602         if (typeof(this.groups[groupId]) == 'undefined') {
25603             return false;
25604         }
25605         
25606         return this.groups[groupId] ;
25607     }
25608     
25609     
25610 });
25611 /*
25612  * - LGPL
25613  *
25614  * RadioItem
25615  * 
25616  */
25617
25618 /**
25619  * @class Roo.bootstrap.form.Radio
25620  * @extends Roo.bootstrap.Component
25621  * Bootstrap Radio class
25622  * @cfg {String} boxLabel - the label associated
25623  * @cfg {String} value - the value of radio
25624  * 
25625  * @constructor
25626  * Create a new Radio
25627  * @param {Object} config The config object
25628  */
25629 Roo.bootstrap.form.Radio = function(config){
25630     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25631     
25632 };
25633
25634 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25635     
25636     boxLabel : '',
25637     
25638     value : '',
25639     
25640     getAutoCreate : function()
25641     {
25642         var cfg = {
25643             tag : 'div',
25644             cls : 'form-group radio',
25645             cn : [
25646                 {
25647                     tag : 'label',
25648                     cls : 'box-label',
25649                     html : this.boxLabel
25650                 }
25651             ]
25652         };
25653         
25654         return cfg;
25655     },
25656     
25657     initEvents : function() 
25658     {
25659         this.parent().register(this);
25660         
25661         this.el.on('click', this.onClick, this);
25662         
25663     },
25664     
25665     onClick : function(e)
25666     {
25667         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25668             this.setChecked(true);
25669         }
25670     },
25671     
25672     setChecked : function(state, suppressEvent)
25673     {
25674         this.parent().setValue(this.value, suppressEvent);
25675         
25676     },
25677     
25678     setBoxLabel : function(v)
25679     {
25680         this.boxLabel = v;
25681         
25682         if(this.rendered){
25683             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25684         }
25685     }
25686     
25687 });
25688  
25689
25690  /*
25691  * - LGPL
25692  *
25693  * Input
25694  * 
25695  */
25696
25697 /**
25698  * @class Roo.bootstrap.form.SecurePass
25699  * @extends Roo.bootstrap.form.Input
25700  * Bootstrap SecurePass class
25701  *
25702  * 
25703  * @constructor
25704  * Create a new SecurePass
25705  * @param {Object} config The config object
25706  */
25707  
25708 Roo.bootstrap.form.SecurePass = function (config) {
25709     // these go here, so the translation tool can replace them..
25710     this.errors = {
25711         PwdEmpty: "Please type a password, and then retype it to confirm.",
25712         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25713         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25714         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25715         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25716         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25717         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25718         TooWeak: "Your password is Too Weak."
25719     },
25720     this.meterLabel = "Password strength:";
25721     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25722     this.meterClass = [
25723         "roo-password-meter-tooweak", 
25724         "roo-password-meter-weak", 
25725         "roo-password-meter-medium", 
25726         "roo-password-meter-strong", 
25727         "roo-password-meter-grey"
25728     ];
25729     
25730     this.errors = {};
25731     
25732     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25733 }
25734
25735 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25736     /**
25737      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25738      * {
25739      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25740      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25741      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25742      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25743      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25744      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25745      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25746      * })
25747      */
25748     // private
25749     
25750     meterWidth: 300,
25751     errorMsg :'',    
25752     errors: false,
25753     imageRoot: '/',
25754     /**
25755      * @cfg {String/Object} Label for the strength meter (defaults to
25756      * 'Password strength:')
25757      */
25758     // private
25759     meterLabel: '',
25760     /**
25761      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25762      * ['Weak', 'Medium', 'Strong'])
25763      */
25764     // private    
25765     pwdStrengths: false,    
25766     // private
25767     strength: 0,
25768     // private
25769     _lastPwd: null,
25770     // private
25771     kCapitalLetter: 0,
25772     kSmallLetter: 1,
25773     kDigit: 2,
25774     kPunctuation: 3,
25775     
25776     insecure: false,
25777     // private
25778     initEvents: function ()
25779     {
25780         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25781
25782         if (this.el.is('input[type=password]') && Roo.isSafari) {
25783             this.el.on('keydown', this.SafariOnKeyDown, this);
25784         }
25785
25786         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25787     },
25788     // private
25789     onRender: function (ct, position)
25790     {
25791         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25792         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25793         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25794
25795         this.trigger.createChild({
25796                    cn: [
25797                     {
25798                     //id: 'PwdMeter',
25799                     tag: 'div',
25800                     cls: 'roo-password-meter-grey col-xs-12',
25801                     style: {
25802                         //width: 0,
25803                         //width: this.meterWidth + 'px'                                                
25804                         }
25805                     },
25806                     {                            
25807                          cls: 'roo-password-meter-text'                          
25808                     }
25809                 ]            
25810         });
25811
25812          
25813         if (this.hideTrigger) {
25814             this.trigger.setDisplayed(false);
25815         }
25816         this.setSize(this.width || '', this.height || '');
25817     },
25818     // private
25819     onDestroy: function ()
25820     {
25821         if (this.trigger) {
25822             this.trigger.removeAllListeners();
25823             this.trigger.remove();
25824         }
25825         if (this.wrap) {
25826             this.wrap.remove();
25827         }
25828         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25829     },
25830     // private
25831     checkStrength: function ()
25832     {
25833         var pwd = this.inputEl().getValue();
25834         if (pwd == this._lastPwd) {
25835             return;
25836         }
25837
25838         var strength;
25839         if (this.ClientSideStrongPassword(pwd)) {
25840             strength = 3;
25841         } else if (this.ClientSideMediumPassword(pwd)) {
25842             strength = 2;
25843         } else if (this.ClientSideWeakPassword(pwd)) {
25844             strength = 1;
25845         } else {
25846             strength = 0;
25847         }
25848         
25849         Roo.log('strength1: ' + strength);
25850         
25851         //var pm = this.trigger.child('div/div/div').dom;
25852         var pm = this.trigger.child('div/div');
25853         pm.removeClass(this.meterClass);
25854         pm.addClass(this.meterClass[strength]);
25855                 
25856         
25857         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25858                 
25859         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25860         
25861         this._lastPwd = pwd;
25862     },
25863     reset: function ()
25864     {
25865         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25866         
25867         this._lastPwd = '';
25868         
25869         var pm = this.trigger.child('div/div');
25870         pm.removeClass(this.meterClass);
25871         pm.addClass('roo-password-meter-grey');        
25872         
25873         
25874         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25875         
25876         pt.innerHTML = '';
25877         this.inputEl().dom.type='password';
25878     },
25879     // private
25880     validateValue: function (value)
25881     {
25882         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25883             return false;
25884         }
25885         if (value.length == 0) {
25886             if (this.allowBlank) {
25887                 this.clearInvalid();
25888                 return true;
25889             }
25890
25891             this.markInvalid(this.errors.PwdEmpty);
25892             this.errorMsg = this.errors.PwdEmpty;
25893             return false;
25894         }
25895         
25896         if(this.insecure){
25897             return true;
25898         }
25899         
25900         if (!value.match(/[\x21-\x7e]+/)) {
25901             this.markInvalid(this.errors.PwdBadChar);
25902             this.errorMsg = this.errors.PwdBadChar;
25903             return false;
25904         }
25905         if (value.length < 6) {
25906             this.markInvalid(this.errors.PwdShort);
25907             this.errorMsg = this.errors.PwdShort;
25908             return false;
25909         }
25910         if (value.length > 16) {
25911             this.markInvalid(this.errors.PwdLong);
25912             this.errorMsg = this.errors.PwdLong;
25913             return false;
25914         }
25915         var strength;
25916         if (this.ClientSideStrongPassword(value)) {
25917             strength = 3;
25918         } else if (this.ClientSideMediumPassword(value)) {
25919             strength = 2;
25920         } else if (this.ClientSideWeakPassword(value)) {
25921             strength = 1;
25922         } else {
25923             strength = 0;
25924         }
25925
25926         
25927         if (strength < 2) {
25928             //this.markInvalid(this.errors.TooWeak);
25929             this.errorMsg = this.errors.TooWeak;
25930             //return false;
25931         }
25932         
25933         
25934         console.log('strength2: ' + strength);
25935         
25936         //var pm = this.trigger.child('div/div/div').dom;
25937         
25938         var pm = this.trigger.child('div/div');
25939         pm.removeClass(this.meterClass);
25940         pm.addClass(this.meterClass[strength]);
25941                 
25942         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25943                 
25944         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25945         
25946         this.errorMsg = ''; 
25947         return true;
25948     },
25949     // private
25950     CharacterSetChecks: function (type)
25951     {
25952         this.type = type;
25953         this.fResult = false;
25954     },
25955     // private
25956     isctype: function (character, type)
25957     {
25958         switch (type) {  
25959             case this.kCapitalLetter:
25960                 if (character >= 'A' && character <= 'Z') {
25961                     return true;
25962                 }
25963                 break;
25964             
25965             case this.kSmallLetter:
25966                 if (character >= 'a' && character <= 'z') {
25967                     return true;
25968                 }
25969                 break;
25970             
25971             case this.kDigit:
25972                 if (character >= '0' && character <= '9') {
25973                     return true;
25974                 }
25975                 break;
25976             
25977             case this.kPunctuation:
25978                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25979                     return true;
25980                 }
25981                 break;
25982             
25983             default:
25984                 return false;
25985         }
25986
25987     },
25988     // private
25989     IsLongEnough: function (pwd, size)
25990     {
25991         return !(pwd == null || isNaN(size) || pwd.length < size);
25992     },
25993     // private
25994     SpansEnoughCharacterSets: function (word, nb)
25995     {
25996         if (!this.IsLongEnough(word, nb))
25997         {
25998             return false;
25999         }
26000
26001         var characterSetChecks = new Array(
26002             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
26003             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
26004         );
26005         
26006         for (var index = 0; index < word.length; ++index) {
26007             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26008                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
26009                     characterSetChecks[nCharSet].fResult = true;
26010                     break;
26011                 }
26012             }
26013         }
26014
26015         var nCharSets = 0;
26016         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26017             if (characterSetChecks[nCharSet].fResult) {
26018                 ++nCharSets;
26019             }
26020         }
26021
26022         if (nCharSets < nb) {
26023             return false;
26024         }
26025         return true;
26026     },
26027     // private
26028     ClientSideStrongPassword: function (pwd)
26029     {
26030         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26031     },
26032     // private
26033     ClientSideMediumPassword: function (pwd)
26034     {
26035         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26036     },
26037     // private
26038     ClientSideWeakPassword: function (pwd)
26039     {
26040         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26041     }
26042           
26043 });Roo.rtf = {}; // namespace
26044 Roo.rtf.Hex = function(hex)
26045 {
26046     this.hexstr = hex;
26047 };
26048 Roo.rtf.Paragraph = function(opts)
26049 {
26050     this.content = []; ///??? is that used?
26051 };Roo.rtf.Span = function(opts)
26052 {
26053     this.value = opts.value;
26054 };
26055
26056 Roo.rtf.Group = function(parent)
26057 {
26058     // we dont want to acutally store parent - it will make debug a nightmare..
26059     this.content = [];
26060     this.cn  = [];
26061      
26062        
26063     
26064 };
26065
26066 Roo.rtf.Group.prototype = {
26067     ignorable : false,
26068     content: false,
26069     cn: false,
26070     addContent : function(node) {
26071         // could set styles...
26072         this.content.push(node);
26073     },
26074     addChild : function(cn)
26075     {
26076         this.cn.push(cn);
26077     },
26078     // only for images really...
26079     toDataURL : function()
26080     {
26081         var mimetype = false;
26082         switch(true) {
26083             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
26084                 mimetype = "image/png";
26085                 break;
26086              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26087                 mimetype = "image/jpeg";
26088                 break;
26089             default :
26090                 return 'about:blank'; // ?? error?
26091         }
26092         
26093         
26094         var hexstring = this.content[this.content.length-1].value;
26095         
26096         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26097             return String.fromCharCode(parseInt(a, 16));
26098         }).join(""));
26099     }
26100     
26101 };
26102 // this looks like it's normally the {rtf{ .... }}
26103 Roo.rtf.Document = function()
26104 {
26105     // we dont want to acutally store parent - it will make debug a nightmare..
26106     this.rtlch  = [];
26107     this.content = [];
26108     this.cn = [];
26109     
26110 };
26111 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
26112     addChild : function(cn)
26113     {
26114         this.cn.push(cn);
26115         switch(cn.type) {
26116             case 'rtlch': // most content seems to be inside this??
26117             case 'listtext':
26118             case 'shpinst':
26119                 this.rtlch.push(cn);
26120                 return;
26121             default:
26122                 this[cn.type] = cn;
26123         }
26124         
26125     },
26126     
26127     getElementsByType : function(type)
26128     {
26129         var ret =  [];
26130         this._getElementsByType(type, ret, this.cn, 'rtf');
26131         return ret;
26132     },
26133     _getElementsByType : function (type, ret, search_array, path)
26134     {
26135         search_array.forEach(function(n,i) {
26136             if (n.type == type) {
26137                 n.path = path + '/' + n.type + ':' + i;
26138                 ret.push(n);
26139             }
26140             if (n.cn.length > 0) {
26141                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26142             }
26143         },this);
26144     }
26145     
26146 });
26147  
26148 Roo.rtf.Ctrl = function(opts)
26149 {
26150     this.value = opts.value;
26151     this.param = opts.param;
26152 };
26153 /**
26154  *
26155  *
26156  * based on this https://github.com/iarna/rtf-parser
26157  * it's really only designed to extract pict from pasted RTF 
26158  *
26159  * usage:
26160  *
26161  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26162  *  
26163  *
26164  */
26165
26166  
26167
26168
26169
26170 Roo.rtf.Parser = function(text) {
26171     //super({objectMode: true})
26172     this.text = '';
26173     this.parserState = this.parseText;
26174     
26175     // these are for interpeter...
26176     this.doc = {};
26177     ///this.parserState = this.parseTop
26178     this.groupStack = [];
26179     this.hexStore = [];
26180     this.doc = false;
26181     
26182     this.groups = []; // where we put the return.
26183     
26184     for (var ii = 0; ii < text.length; ++ii) {
26185         ++this.cpos;
26186         
26187         if (text[ii] === '\n') {
26188             ++this.row;
26189             this.col = 1;
26190         } else {
26191             ++this.col;
26192         }
26193         this.parserState(text[ii]);
26194     }
26195     
26196     
26197     
26198 };
26199 Roo.rtf.Parser.prototype = {
26200     text : '', // string being parsed..
26201     controlWord : '',
26202     controlWordParam :  '',
26203     hexChar : '',
26204     doc : false,
26205     group: false,
26206     groupStack : false,
26207     hexStore : false,
26208     
26209     
26210     cpos : 0, 
26211     row : 1, // reportin?
26212     col : 1, //
26213
26214      
26215     push : function (el)
26216     {
26217         var m = 'cmd'+ el.type;
26218         if (typeof(this[m]) == 'undefined') {
26219             Roo.log('invalid cmd:' + el.type);
26220             return;
26221         }
26222         this[m](el);
26223         //Roo.log(el);
26224     },
26225     flushHexStore : function()
26226     {
26227         if (this.hexStore.length < 1) {
26228             return;
26229         }
26230         var hexstr = this.hexStore.map(
26231             function(cmd) {
26232                 return cmd.value;
26233         }).join('');
26234         
26235         this.group.addContent( new Roo.rtf.Hex( hexstr ));
26236               
26237             
26238         this.hexStore.splice(0)
26239         
26240     },
26241     
26242     cmdgroupstart : function()
26243     {
26244         this.flushHexStore();
26245         if (this.group) {
26246             this.groupStack.push(this.group);
26247         }
26248          // parent..
26249         if (this.doc === false) {
26250             this.group = this.doc = new Roo.rtf.Document();
26251             return;
26252             
26253         }
26254         this.group = new Roo.rtf.Group(this.group);
26255     },
26256     cmdignorable : function()
26257     {
26258         this.flushHexStore();
26259         this.group.ignorable = true;
26260     },
26261     cmdendparagraph : function()
26262     {
26263         this.flushHexStore();
26264         this.group.addContent(new Roo.rtf.Paragraph());
26265     },
26266     cmdgroupend : function ()
26267     {
26268         this.flushHexStore();
26269         var endingGroup = this.group;
26270         
26271         
26272         this.group = this.groupStack.pop();
26273         if (this.group) {
26274             this.group.addChild(endingGroup);
26275         }
26276         
26277         
26278         
26279         var doc = this.group || this.doc;
26280         //if (endingGroup instanceof FontTable) {
26281         //  doc.fonts = endingGroup.table
26282         //} else if (endingGroup instanceof ColorTable) {
26283         //  doc.colors = endingGroup.table
26284         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26285         if (endingGroup.ignorable === false) {
26286             //code
26287             this.groups.push(endingGroup);
26288            // Roo.log( endingGroup );
26289         }
26290             //Roo.each(endingGroup.content, function(item)) {
26291             //    doc.addContent(item);
26292             //}
26293             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26294         //}
26295     },
26296     cmdtext : function (cmd)
26297     {
26298         this.flushHexStore();
26299         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26300             //this.group = this.doc
26301             return;  // we really don't care about stray text...
26302         }
26303         this.group.addContent(new Roo.rtf.Span(cmd));
26304     },
26305     cmdcontrolword : function (cmd)
26306     {
26307         this.flushHexStore();
26308         if (!this.group.type) {
26309             this.group.type = cmd.value;
26310             return;
26311         }
26312         this.group.addContent(new Roo.rtf.Ctrl(cmd));
26313         // we actually don't care about ctrl words...
26314         return ;
26315         /*
26316         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26317         if (this[method]) {
26318             this[method](cmd.param)
26319         } else {
26320             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26321         }
26322         */
26323     },
26324     cmdhexchar : function(cmd) {
26325         this.hexStore.push(cmd);
26326     },
26327     cmderror : function(cmd) {
26328         throw cmd.value;
26329     },
26330     
26331     /*
26332       _flush (done) {
26333         if (this.text !== '\u0000') this.emitText()
26334         done()
26335       }
26336       */
26337       
26338       
26339     parseText : function(c)
26340     {
26341         if (c === '\\') {
26342             this.parserState = this.parseEscapes;
26343         } else if (c === '{') {
26344             this.emitStartGroup();
26345         } else if (c === '}') {
26346             this.emitEndGroup();
26347         } else if (c === '\x0A' || c === '\x0D') {
26348             // cr/lf are noise chars
26349         } else {
26350             this.text += c;
26351         }
26352     },
26353     
26354     parseEscapes: function (c)
26355     {
26356         if (c === '\\' || c === '{' || c === '}') {
26357             this.text += c;
26358             this.parserState = this.parseText;
26359         } else {
26360             this.parserState = this.parseControlSymbol;
26361             this.parseControlSymbol(c);
26362         }
26363     },
26364     parseControlSymbol: function(c)
26365     {
26366         if (c === '~') {
26367             this.text += '\u00a0'; // nbsp
26368             this.parserState = this.parseText
26369         } else if (c === '-') {
26370              this.text += '\u00ad'; // soft hyphen
26371         } else if (c === '_') {
26372             this.text += '\u2011'; // non-breaking hyphen
26373         } else if (c === '*') {
26374             this.emitIgnorable();
26375             this.parserState = this.parseText;
26376         } else if (c === "'") {
26377             this.parserState = this.parseHexChar;
26378         } else if (c === '|') { // formula cacter
26379             this.emitFormula();
26380             this.parserState = this.parseText;
26381         } else if (c === ':') { // subentry in an index entry
26382             this.emitIndexSubEntry();
26383             this.parserState = this.parseText;
26384         } else if (c === '\x0a') {
26385             this.emitEndParagraph();
26386             this.parserState = this.parseText;
26387         } else if (c === '\x0d') {
26388             this.emitEndParagraph();
26389             this.parserState = this.parseText;
26390         } else {
26391             this.parserState = this.parseControlWord;
26392             this.parseControlWord(c);
26393         }
26394     },
26395     parseHexChar: function (c)
26396     {
26397         if (/^[A-Fa-f0-9]$/.test(c)) {
26398             this.hexChar += c;
26399             if (this.hexChar.length >= 2) {
26400               this.emitHexChar();
26401               this.parserState = this.parseText;
26402             }
26403             return;
26404         }
26405         this.emitError("Invalid character \"" + c + "\" in hex literal.");
26406         this.parserState = this.parseText;
26407         
26408     },
26409     parseControlWord : function(c)
26410     {
26411         if (c === ' ') {
26412             this.emitControlWord();
26413             this.parserState = this.parseText;
26414         } else if (/^[-\d]$/.test(c)) {
26415             this.parserState = this.parseControlWordParam;
26416             this.controlWordParam += c;
26417         } else if (/^[A-Za-z]$/.test(c)) {
26418           this.controlWord += c;
26419         } else {
26420           this.emitControlWord();
26421           this.parserState = this.parseText;
26422           this.parseText(c);
26423         }
26424     },
26425     parseControlWordParam : function (c) {
26426         if (/^\d$/.test(c)) {
26427           this.controlWordParam += c;
26428         } else if (c === ' ') {
26429           this.emitControlWord();
26430           this.parserState = this.parseText;
26431         } else {
26432           this.emitControlWord();
26433           this.parserState = this.parseText;
26434           this.parseText(c);
26435         }
26436     },
26437     
26438     
26439     
26440     
26441     emitText : function () {
26442         if (this.text === '') {
26443             return;
26444         }
26445         this.push({
26446             type: 'text',
26447             value: this.text,
26448             pos: this.cpos,
26449             row: this.row,
26450             col: this.col
26451         });
26452         this.text = ''
26453     },
26454     emitControlWord : function ()
26455     {
26456         this.emitText();
26457         if (this.controlWord === '') {
26458             // do we want to track this - it seems just to cause problems.
26459             //this.emitError('empty control word');
26460         } else {
26461             this.push({
26462                   type: 'controlword',
26463                   value: this.controlWord,
26464                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
26465                   pos: this.cpos,
26466                   row: this.row,
26467                   col: this.col
26468             });
26469         }
26470         this.controlWord = '';
26471         this.controlWordParam = '';
26472     },
26473     emitStartGroup : function ()
26474     {
26475         this.emitText();
26476         this.push({
26477             type: 'groupstart',
26478             pos: this.cpos,
26479             row: this.row,
26480             col: this.col
26481         });
26482     },
26483     emitEndGroup : function ()
26484     {
26485         this.emitText();
26486         this.push({
26487             type: 'groupend',
26488             pos: this.cpos,
26489             row: this.row,
26490             col: this.col
26491         });
26492     },
26493     emitIgnorable : function ()
26494     {
26495         this.emitText();
26496         this.push({
26497             type: 'ignorable',
26498             pos: this.cpos,
26499             row: this.row,
26500             col: this.col
26501         });
26502     },
26503     emitHexChar : function ()
26504     {
26505         this.emitText();
26506         this.push({
26507             type: 'hexchar',
26508             value: this.hexChar,
26509             pos: this.cpos,
26510             row: this.row,
26511             col: this.col
26512         });
26513         this.hexChar = ''
26514     },
26515     emitError : function (message)
26516     {
26517       this.emitText();
26518       this.push({
26519             type: 'error',
26520             value: message,
26521             row: this.row,
26522             col: this.col,
26523             char: this.cpos //,
26524             //stack: new Error().stack
26525         });
26526     },
26527     emitEndParagraph : function () {
26528         this.emitText();
26529         this.push({
26530             type: 'endparagraph',
26531             pos: this.cpos,
26532             row: this.row,
26533             col: this.col
26534         });
26535     }
26536      
26537 } ;
26538 Roo.htmleditor = {};
26539  
26540 /**
26541  * @class Roo.htmleditor.Filter
26542  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26543  * @cfg {DomElement} node The node to iterate and filter
26544  * @cfg {boolean|String|Array} tag Tags to replace 
26545  * @constructor
26546  * Create a new Filter.
26547  * @param {Object} config Configuration options
26548  */
26549
26550
26551
26552 Roo.htmleditor.Filter = function(cfg) {
26553     Roo.apply(this.cfg);
26554     // this does not actually call walk as it's really just a abstract class
26555 }
26556
26557
26558 Roo.htmleditor.Filter.prototype = {
26559     
26560     node: false,
26561     
26562     tag: false,
26563
26564     // overrride to do replace comments.
26565     replaceComment : false,
26566     
26567     // overrride to do replace or do stuff with tags..
26568     replaceTag : false,
26569     
26570     walk : function(dom)
26571     {
26572         Roo.each( Array.from(dom.childNodes), function( e ) {
26573             switch(true) {
26574                 
26575                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26576                     this.replaceComment(e);
26577                     return;
26578                 
26579                 case e.nodeType != 1: //not a node.
26580                     return;
26581                 
26582                 case this.tag === true: // everything
26583                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26584                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26585                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26586                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26587                     if (this.replaceTag && false === this.replaceTag(e)) {
26588                         return;
26589                     }
26590                     if (e.hasChildNodes()) {
26591                         this.walk(e);
26592                     }
26593                     return;
26594                 
26595                 default:    // tags .. that do not match.
26596                     if (e.hasChildNodes()) {
26597                         this.walk(e);
26598                     }
26599             }
26600             
26601         }, this);
26602         
26603     },
26604     
26605     
26606     removeNodeKeepChildren : function( node)
26607     {
26608     
26609         ar = Array.from(node.childNodes);
26610         for (var i = 0; i < ar.length; i++) {
26611          
26612             node.removeChild(ar[i]);
26613             // what if we need to walk these???
26614             node.parentNode.insertBefore(ar[i], node);
26615            
26616         }
26617         node.parentNode.removeChild(node);
26618     }
26619 }; 
26620
26621 /**
26622  * @class Roo.htmleditor.FilterAttributes
26623  * clean attributes and  styles including http:// etc.. in attribute
26624  * @constructor
26625 * Run a new Attribute Filter
26626 * @param {Object} config Configuration options
26627  */
26628 Roo.htmleditor.FilterAttributes = function(cfg)
26629 {
26630     Roo.apply(this, cfg);
26631     this.attrib_black = this.attrib_black || [];
26632     this.attrib_white = this.attrib_white || [];
26633
26634     this.attrib_clean = this.attrib_clean || [];
26635     this.style_white = this.style_white || [];
26636     this.style_black = this.style_black || [];
26637     this.walk(cfg.node);
26638 }
26639
26640 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26641 {
26642     tag: true, // all tags
26643     
26644     attrib_black : false, // array
26645     attrib_clean : false,
26646     attrib_white : false,
26647
26648     style_white : false,
26649     style_black : false,
26650      
26651      
26652     replaceTag : function(node)
26653     {
26654         if (!node.attributes || !node.attributes.length) {
26655             return true;
26656         }
26657         
26658         for (var i = node.attributes.length-1; i > -1 ; i--) {
26659             var a = node.attributes[i];
26660             //console.log(a);
26661             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26662                 node.removeAttribute(a.name);
26663                 continue;
26664             }
26665             
26666             
26667             
26668             if (a.name.toLowerCase().substr(0,2)=='on')  {
26669                 node.removeAttribute(a.name);
26670                 continue;
26671             }
26672             
26673             
26674             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26675                 node.removeAttribute(a.name);
26676                 continue;
26677             }
26678             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26679                 this.cleanAttr(node,a.name,a.value); // fixme..
26680                 continue;
26681             }
26682             if (a.name == 'style') {
26683                 this.cleanStyle(node,a.name,a.value);
26684                 continue;
26685             }
26686             /// clean up MS crap..
26687             // tecnically this should be a list of valid class'es..
26688             
26689             
26690             if (a.name == 'class') {
26691                 if (a.value.match(/^Mso/)) {
26692                     node.removeAttribute('class');
26693                 }
26694                 
26695                 if (a.value.match(/^body$/)) {
26696                     node.removeAttribute('class');
26697                 }
26698                 continue;
26699             }
26700             
26701             
26702             // style cleanup!?
26703             // class cleanup?
26704             
26705         }
26706         return true; // clean children
26707     },
26708         
26709     cleanAttr: function(node, n,v)
26710     {
26711         
26712         if (v.match(/^\./) || v.match(/^\//)) {
26713             return;
26714         }
26715         if (v.match(/^(http|https):\/\//)
26716             || v.match(/^mailto:/) 
26717             || v.match(/^ftp:/)
26718             || v.match(/^data:/)
26719             ) {
26720             return;
26721         }
26722         if (v.match(/^#/)) {
26723             return;
26724         }
26725         if (v.match(/^\{/)) { // allow template editing.
26726             return;
26727         }
26728 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26729         node.removeAttribute(n);
26730         
26731     },
26732     cleanStyle : function(node,  n,v)
26733     {
26734         if (v.match(/expression/)) { //XSS?? should we even bother..
26735             node.removeAttribute(n);
26736             return;
26737         }
26738         
26739         var parts = v.split(/;/);
26740         var clean = [];
26741         
26742         Roo.each(parts, function(p) {
26743             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26744             if (!p.length) {
26745                 return true;
26746             }
26747             var l = p.split(':').shift().replace(/\s+/g,'');
26748             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26749             
26750             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26751                 return true;
26752             }
26753             //Roo.log()
26754             // only allow 'c whitelisted system attributes'
26755             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26756                 return true;
26757             }
26758             
26759             
26760             clean.push(p);
26761             return true;
26762         },this);
26763         if (clean.length) { 
26764             node.setAttribute(n, clean.join(';'));
26765         } else {
26766             node.removeAttribute(n);
26767         }
26768         
26769     }
26770         
26771         
26772         
26773     
26774 });/**
26775  * @class Roo.htmleditor.FilterBlack
26776  * remove blacklisted elements.
26777  * @constructor
26778  * Run a new Blacklisted Filter
26779  * @param {Object} config Configuration options
26780  */
26781
26782 Roo.htmleditor.FilterBlack = function(cfg)
26783 {
26784     Roo.apply(this, cfg);
26785     this.walk(cfg.node);
26786 }
26787
26788 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26789 {
26790     tag : true, // all elements.
26791    
26792     replaceTag : function(n)
26793     {
26794         n.parentNode.removeChild(n);
26795     }
26796 });
26797 /**
26798  * @class Roo.htmleditor.FilterComment
26799  * remove comments.
26800  * @constructor
26801 * Run a new Comments Filter
26802 * @param {Object} config Configuration options
26803  */
26804 Roo.htmleditor.FilterComment = function(cfg)
26805 {
26806     this.walk(cfg.node);
26807 }
26808
26809 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26810 {
26811   
26812     replaceComment : function(n)
26813     {
26814         n.parentNode.removeChild(n);
26815     }
26816 });/**
26817  * @class Roo.htmleditor.FilterKeepChildren
26818  * remove tags but keep children
26819  * @constructor
26820  * Run a new Keep Children Filter
26821  * @param {Object} config Configuration options
26822  */
26823
26824 Roo.htmleditor.FilterKeepChildren = function(cfg)
26825 {
26826     Roo.apply(this, cfg);
26827     if (this.tag === false) {
26828         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26829     }
26830     // hacky?
26831     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26832         this.cleanNamespace = true;
26833     }
26834         
26835     this.walk(cfg.node);
26836 }
26837
26838 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26839 {
26840     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26841   
26842     replaceTag : function(node)
26843     {
26844         // walk children...
26845         //Roo.log(node.tagName);
26846         var ar = Array.from(node.childNodes);
26847         //remove first..
26848         
26849         for (var i = 0; i < ar.length; i++) {
26850             var e = ar[i];
26851             if (e.nodeType == 1) {
26852                 if (
26853                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26854                     || // array and it matches
26855                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26856                     ||
26857                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26858                     ||
26859                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26860                 ) {
26861                     this.replaceTag(ar[i]); // child is blacklisted as well...
26862                     continue;
26863                 }
26864             }
26865         }  
26866         ar = Array.from(node.childNodes);
26867         for (var i = 0; i < ar.length; i++) {
26868          
26869             node.removeChild(ar[i]);
26870             // what if we need to walk these???
26871             node.parentNode.insertBefore(ar[i], node);
26872             if (this.tag !== false) {
26873                 this.walk(ar[i]);
26874                 
26875             }
26876         }
26877         //Roo.log("REMOVE:" + node.tagName);
26878         node.parentNode.removeChild(node);
26879         return false; // don't walk children
26880         
26881         
26882     }
26883 });/**
26884  * @class Roo.htmleditor.FilterParagraph
26885  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26886  * like on 'push' to remove the <p> tags and replace them with line breaks.
26887  * @constructor
26888  * Run a new Paragraph Filter
26889  * @param {Object} config Configuration options
26890  */
26891
26892 Roo.htmleditor.FilterParagraph = function(cfg)
26893 {
26894     // no need to apply config.
26895     this.walk(cfg.node);
26896 }
26897
26898 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26899 {
26900     
26901      
26902     tag : 'P',
26903     
26904      
26905     replaceTag : function(node)
26906     {
26907         
26908         if (node.childNodes.length == 1 &&
26909             node.childNodes[0].nodeType == 3 &&
26910             node.childNodes[0].textContent.trim().length < 1
26911             ) {
26912             // remove and replace with '<BR>';
26913             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26914             return false; // no need to walk..
26915         }
26916         var ar = Array.from(node.childNodes);
26917         for (var i = 0; i < ar.length; i++) {
26918             node.removeChild(ar[i]);
26919             // what if we need to walk these???
26920             node.parentNode.insertBefore(ar[i], node);
26921         }
26922         // now what about this?
26923         // <p> &nbsp; </p>
26924         
26925         // double BR.
26926         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26927         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26928         node.parentNode.removeChild(node);
26929         
26930         return false;
26931
26932     }
26933     
26934 });/**
26935  * @class Roo.htmleditor.FilterSpan
26936  * filter span's with no attributes out..
26937  * @constructor
26938  * Run a new Span Filter
26939  * @param {Object} config Configuration options
26940  */
26941
26942 Roo.htmleditor.FilterSpan = function(cfg)
26943 {
26944     // no need to apply config.
26945     this.walk(cfg.node);
26946 }
26947
26948 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26949 {
26950      
26951     tag : 'SPAN',
26952      
26953  
26954     replaceTag : function(node)
26955     {
26956         if (node.attributes && node.attributes.length > 0) {
26957             return true; // walk if there are any.
26958         }
26959         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26960         return false;
26961      
26962     }
26963     
26964 });/**
26965  * @class Roo.htmleditor.FilterTableWidth
26966   try and remove table width data - as that frequently messes up other stuff.
26967  * 
26968  *      was cleanTableWidths.
26969  *
26970  * Quite often pasting from word etc.. results in tables with column and widths.
26971  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26972  *
26973  * @constructor
26974  * Run a new Table Filter
26975  * @param {Object} config Configuration options
26976  */
26977
26978 Roo.htmleditor.FilterTableWidth = function(cfg)
26979 {
26980     // no need to apply config.
26981     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26982     this.walk(cfg.node);
26983 }
26984
26985 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26986 {
26987      
26988      
26989     
26990     replaceTag: function(node) {
26991         
26992         
26993       
26994         if (node.hasAttribute('width')) {
26995             node.removeAttribute('width');
26996         }
26997         
26998          
26999         if (node.hasAttribute("style")) {
27000             // pretty basic...
27001             
27002             var styles = node.getAttribute("style").split(";");
27003             var nstyle = [];
27004             Roo.each(styles, function(s) {
27005                 if (!s.match(/:/)) {
27006                     return;
27007                 }
27008                 var kv = s.split(":");
27009                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27010                     return;
27011                 }
27012                 // what ever is left... we allow.
27013                 nstyle.push(s);
27014             });
27015             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27016             if (!nstyle.length) {
27017                 node.removeAttribute('style');
27018             }
27019         }
27020         
27021         return true; // continue doing children..
27022     }
27023 });/**
27024  * @class Roo.htmleditor.FilterWord
27025  * try and clean up all the mess that Word generates.
27026  * 
27027  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
27028  
27029  * @constructor
27030  * Run a new Span Filter
27031  * @param {Object} config Configuration options
27032  */
27033
27034 Roo.htmleditor.FilterWord = function(cfg)
27035 {
27036     // no need to apply config.
27037     this.replaceDocBullets(cfg.node);
27038     
27039     this.replaceAname(cfg.node);
27040     // this is disabled as the removal is done by other filters;
27041    // this.walk(cfg.node);
27042     
27043     
27044 }
27045
27046 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27047 {
27048     tag: true,
27049      
27050     
27051     /**
27052      * Clean up MS wordisms...
27053      */
27054     replaceTag : function(node)
27055     {
27056          
27057         // no idea what this does - span with text, replaceds with just text.
27058         if(
27059                 node.nodeName == 'SPAN' &&
27060                 !node.hasAttributes() &&
27061                 node.childNodes.length == 1 &&
27062                 node.firstChild.nodeName == "#text"  
27063         ) {
27064             var textNode = node.firstChild;
27065             node.removeChild(textNode);
27066             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27067                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27068             }
27069             node.parentNode.insertBefore(textNode, node);
27070             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27071                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27072             }
27073             
27074             node.parentNode.removeChild(node);
27075             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27076         }
27077         
27078    
27079         
27080         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27081             node.parentNode.removeChild(node);
27082             return false; // dont do chidlren
27083         }
27084         //Roo.log(node.tagName);
27085         // remove - but keep children..
27086         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27087             //Roo.log('-- removed');
27088             while (node.childNodes.length) {
27089                 var cn = node.childNodes[0];
27090                 node.removeChild(cn);
27091                 node.parentNode.insertBefore(cn, node);
27092                 // move node to parent - and clean it..
27093                 if (cn.nodeType == 1) {
27094                     this.replaceTag(cn);
27095                 }
27096                 
27097             }
27098             node.parentNode.removeChild(node);
27099             /// no need to iterate chidlren = it's got none..
27100             //this.iterateChildren(node, this.cleanWord);
27101             return false; // no need to iterate children.
27102         }
27103         // clean styles
27104         if (node.className.length) {
27105             
27106             var cn = node.className.split(/\W+/);
27107             var cna = [];
27108             Roo.each(cn, function(cls) {
27109                 if (cls.match(/Mso[a-zA-Z]+/)) {
27110                     return;
27111                 }
27112                 cna.push(cls);
27113             });
27114             node.className = cna.length ? cna.join(' ') : '';
27115             if (!cna.length) {
27116                 node.removeAttribute("class");
27117             }
27118         }
27119         
27120         if (node.hasAttribute("lang")) {
27121             node.removeAttribute("lang");
27122         }
27123         
27124         if (node.hasAttribute("style")) {
27125             
27126             var styles = node.getAttribute("style").split(";");
27127             var nstyle = [];
27128             Roo.each(styles, function(s) {
27129                 if (!s.match(/:/)) {
27130                     return;
27131                 }
27132                 var kv = s.split(":");
27133                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27134                     return;
27135                 }
27136                 // what ever is left... we allow.
27137                 nstyle.push(s);
27138             });
27139             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27140             if (!nstyle.length) {
27141                 node.removeAttribute('style');
27142             }
27143         }
27144         return true; // do children
27145         
27146         
27147         
27148     },
27149     
27150     styleToObject: function(node)
27151     {
27152         var styles = (node.getAttribute("style") || '').split(";");
27153         var ret = {};
27154         Roo.each(styles, function(s) {
27155             if (!s.match(/:/)) {
27156                 return;
27157             }
27158             var kv = s.split(":");
27159              
27160             // what ever is left... we allow.
27161             ret[kv[0].trim()] = kv[1];
27162         });
27163         return ret;
27164     },
27165     
27166     
27167     replaceAname : function (doc)
27168     {
27169         // replace all the a/name without..
27170         var aa = Array.from(doc.getElementsByTagName('a'));
27171         for (var i = 0; i  < aa.length; i++) {
27172             var a = aa[i];
27173             if (a.hasAttribute("name")) {
27174                 a.removeAttribute("name");
27175             }
27176             if (a.hasAttribute("href")) {
27177                 continue;
27178             }
27179             // reparent children.
27180             this.removeNodeKeepChildren(a);
27181             
27182         }
27183         
27184         
27185         
27186     },
27187
27188     
27189     
27190     replaceDocBullets : function(doc)
27191     {
27192         // this is a bit odd - but it appears some indents use ql-indent-1
27193          //Roo.log(doc.innerHTML);
27194         
27195         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27196         for( var i = 0; i < listpara.length; i ++) {
27197             listpara[i].className = "MsoListParagraph";
27198         }
27199         
27200         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27201         for( var i = 0; i < listpara.length; i ++) {
27202             listpara[i].className = "MsoListParagraph";
27203         }
27204         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27205         for( var i = 0; i < listpara.length; i ++) {
27206             listpara[i].className = "MsoListParagraph";
27207         }
27208         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
27209         for( var i = 0; i < listpara.length; i ++) {
27210             listpara[i].className = "MsoListParagraph";
27211         }
27212         
27213         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27214         var htwo =  Array.from(doc.getElementsByTagName('h2'));
27215         for( var i = 0; i < htwo.length; i ++) {
27216             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27217                 htwo[i].className = "MsoListParagraph";
27218             }
27219         }
27220         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
27221         for( var i = 0; i < listpara.length; i ++) {
27222             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27223                 listpara[i].className = "MsoListParagraph";
27224             } else {
27225                 listpara[i].className = "MsoNormalx";
27226             }
27227         }
27228        
27229         listpara = doc.getElementsByClassName('MsoListParagraph');
27230         // Roo.log(doc.innerHTML);
27231         
27232         
27233         
27234         while(listpara.length) {
27235             
27236             this.replaceDocBullet(listpara.item(0));
27237         }
27238       
27239     },
27240     
27241      
27242     
27243     replaceDocBullet : function(p)
27244     {
27245         // gather all the siblings.
27246         var ns = p,
27247             parent = p.parentNode,
27248             doc = parent.ownerDocument,
27249             items = [];
27250             
27251         var listtype = 'ul';   
27252         while (ns) {
27253             if (ns.nodeType != 1) {
27254                 ns = ns.nextSibling;
27255                 continue;
27256             }
27257             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27258                 break;
27259             }
27260             var spans = ns.getElementsByTagName('span');
27261             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27262                 items.push(ns);
27263                 ns = ns.nextSibling;
27264                 has_list = true;
27265                 if (spans.length && spans[0].hasAttribute('style')) {
27266                     var  style = this.styleToObject(spans[0]);
27267                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
27268                         listtype = 'ol';
27269                     }
27270                 }
27271                 
27272                 continue;
27273             }
27274             var spans = ns.getElementsByTagName('span');
27275             if (!spans.length) {
27276                 break;
27277             }
27278             var has_list  = false;
27279             for(var i = 0; i < spans.length; i++) {
27280                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27281                     has_list = true;
27282                     break;
27283                 }
27284             }
27285             if (!has_list) {
27286                 break;
27287             }
27288             items.push(ns);
27289             ns = ns.nextSibling;
27290             
27291             
27292         }
27293         if (!items.length) {
27294             ns.className = "";
27295             return;
27296         }
27297         
27298         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27299         parent.insertBefore(ul, p);
27300         var lvl = 0;
27301         var stack = [ ul ];
27302         var last_li = false;
27303         
27304         var margin_to_depth = {};
27305         max_margins = -1;
27306         
27307         items.forEach(function(n, ipos) {
27308             //Roo.log("got innertHMLT=" + n.innerHTML);
27309             
27310             var spans = n.getElementsByTagName('span');
27311             if (!spans.length) {
27312                 //Roo.log("No spans found");
27313                  
27314                 parent.removeChild(n);
27315                 
27316                 
27317                 return; // skip it...
27318             }
27319            
27320                 
27321             var num = 1;
27322             var style = {};
27323             for(var i = 0; i < spans.length; i++) {
27324             
27325                 style = this.styleToObject(spans[i]);
27326                 if (typeof(style['mso-list']) == 'undefined') {
27327                     continue;
27328                 }
27329                 if (listtype == 'ol') {
27330                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
27331                 }
27332                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27333                 break;
27334             }
27335             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27336             style = this.styleToObject(n); // mo-list is from the parent node.
27337             if (typeof(style['mso-list']) == 'undefined') {
27338                 //Roo.log("parent is missing level");
27339                   
27340                 parent.removeChild(n);
27341                  
27342                 return;
27343             }
27344             
27345             var margin = style['margin-left'];
27346             if (typeof(margin_to_depth[margin]) == 'undefined') {
27347                 max_margins++;
27348                 margin_to_depth[margin] = max_margins;
27349             }
27350             nlvl = margin_to_depth[margin] ;
27351              
27352             if (nlvl > lvl) {
27353                 //new indent
27354                 var nul = doc.createElement(listtype); // what about number lists...
27355                 if (!last_li) {
27356                     last_li = doc.createElement('li');
27357                     stack[lvl].appendChild(last_li);
27358                 }
27359                 last_li.appendChild(nul);
27360                 stack[nlvl] = nul;
27361                 
27362             }
27363             lvl = nlvl;
27364             
27365             // not starting at 1..
27366             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27367                 stack[nlvl].setAttribute("start", num);
27368             }
27369             
27370             var nli = stack[nlvl].appendChild(doc.createElement('li'));
27371             last_li = nli;
27372             nli.innerHTML = n.innerHTML;
27373             //Roo.log("innerHTML = " + n.innerHTML);
27374             parent.removeChild(n);
27375             
27376              
27377              
27378             
27379         },this);
27380         
27381         
27382         
27383         
27384     }
27385     
27386     
27387     
27388 });
27389 /**
27390  * @class Roo.htmleditor.FilterStyleToTag
27391  * part of the word stuff... - certain 'styles' should be converted to tags.
27392  * eg.
27393  *   font-weight: bold -> bold
27394  *   ?? super / subscrit etc..
27395  * 
27396  * @constructor
27397 * Run a new style to tag filter.
27398 * @param {Object} config Configuration options
27399  */
27400 Roo.htmleditor.FilterStyleToTag = function(cfg)
27401 {
27402     
27403     this.tags = {
27404         B  : [ 'fontWeight' , 'bold'],
27405         I :  [ 'fontStyle' , 'italic'],
27406         //pre :  [ 'font-style' , 'italic'],
27407         // h1.. h6 ?? font-size?
27408         SUP : [ 'verticalAlign' , 'super' ],
27409         SUB : [ 'verticalAlign' , 'sub' ]
27410         
27411         
27412     };
27413     
27414     Roo.apply(this, cfg);
27415      
27416     
27417     this.walk(cfg.node);
27418     
27419     
27420     
27421 }
27422
27423
27424 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27425 {
27426     tag: true, // all tags
27427     
27428     tags : false,
27429     
27430     
27431     replaceTag : function(node)
27432     {
27433         
27434         
27435         if (node.getAttribute("style") === null) {
27436             return true;
27437         }
27438         var inject = [];
27439         for (var k in this.tags) {
27440             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27441                 inject.push(k);
27442                 node.style.removeProperty(this.tags[k][0]);
27443             }
27444         }
27445         if (!inject.length) {
27446             return true; 
27447         }
27448         var cn = Array.from(node.childNodes);
27449         var nn = node;
27450         Roo.each(inject, function(t) {
27451             var nc = node.ownerDocument.createElement(t);
27452             nn.appendChild(nc);
27453             nn = nc;
27454         });
27455         for(var i = 0;i < cn.length;cn++) {
27456             node.removeChild(cn[i]);
27457             nn.appendChild(cn[i]);
27458         }
27459         return true /// iterate thru
27460     }
27461     
27462 })/**
27463  * @class Roo.htmleditor.FilterLongBr
27464  * BR/BR/BR - keep a maximum of 2...
27465  * @constructor
27466  * Run a new Long BR Filter
27467  * @param {Object} config Configuration options
27468  */
27469
27470 Roo.htmleditor.FilterLongBr = function(cfg)
27471 {
27472     // no need to apply config.
27473     this.walk(cfg.node);
27474 }
27475
27476 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27477 {
27478     
27479      
27480     tag : 'BR',
27481     
27482      
27483     replaceTag : function(node)
27484     {
27485         
27486         var ps = node.nextSibling;
27487         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27488             ps = ps.nextSibling;
27489         }
27490         
27491         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
27492             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27493             return false;
27494         }
27495         
27496         if (!ps || ps.nodeType != 1) {
27497             return false;
27498         }
27499         
27500         if (!ps || ps.tagName != 'BR') {
27501            
27502             return false;
27503         }
27504         
27505         
27506         
27507         
27508         
27509         if (!node.previousSibling) {
27510             return false;
27511         }
27512         var ps = node.previousSibling;
27513         
27514         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27515             ps = ps.previousSibling;
27516         }
27517         if (!ps || ps.nodeType != 1) {
27518             return false;
27519         }
27520         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27521         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27522             return false;
27523         }
27524         
27525         node.parentNode.removeChild(node); // remove me...
27526         
27527         return false; // no need to do children
27528
27529     }
27530     
27531 }); 
27532
27533 /**
27534  * @class Roo.htmleditor.FilterBlock
27535  * removes id / data-block and contenteditable that are associated with blocks
27536  * usage should be done on a cloned copy of the dom
27537  * @constructor
27538 * Run a new Attribute Filter { node : xxxx }}
27539 * @param {Object} config Configuration options
27540  */
27541 Roo.htmleditor.FilterBlock = function(cfg)
27542 {
27543     Roo.apply(this, cfg);
27544     var qa = cfg.node.querySelectorAll;
27545     this.removeAttributes('data-block');
27546     this.removeAttributes('contenteditable');
27547     this.removeAttributes('id');
27548     
27549 }
27550
27551 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27552 {
27553     node: true, // all tags
27554      
27555      
27556     removeAttributes : function(attr)
27557     {
27558         var ar = this.node.querySelectorAll('*[' + attr + ']');
27559         for (var i =0;i<ar.length;i++) {
27560             ar[i].removeAttribute(attr);
27561         }
27562     }
27563         
27564         
27565         
27566     
27567 });
27568 /***
27569  * This is based loosely on tinymce 
27570  * @class Roo.htmleditor.TidySerializer
27571  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27572  * @constructor
27573  * @method Serializer
27574  * @param {Object} settings Name/value settings object.
27575  */
27576
27577
27578 Roo.htmleditor.TidySerializer = function(settings)
27579 {
27580     Roo.apply(this, settings);
27581     
27582     this.writer = new Roo.htmleditor.TidyWriter(settings);
27583     
27584     
27585
27586 };
27587 Roo.htmleditor.TidySerializer.prototype = {
27588     
27589     /**
27590      * @param {boolean} inner do the inner of the node.
27591      */
27592     inner : false,
27593     
27594     writer : false,
27595     
27596     /**
27597     * Serializes the specified node into a string.
27598     *
27599     * @example
27600     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27601     * @method serialize
27602     * @param {DomElement} node Node instance to serialize.
27603     * @return {String} String with HTML based on DOM tree.
27604     */
27605     serialize : function(node) {
27606         
27607         // = settings.validate;
27608         var writer = this.writer;
27609         var self  = this;
27610         this.handlers = {
27611             // #text
27612             3: function(node) {
27613                 
27614                 writer.text(node.nodeValue, node);
27615             },
27616             // #comment
27617             8: function(node) {
27618                 writer.comment(node.nodeValue);
27619             },
27620             // Processing instruction
27621             7: function(node) {
27622                 writer.pi(node.name, node.nodeValue);
27623             },
27624             // Doctype
27625             10: function(node) {
27626                 writer.doctype(node.nodeValue);
27627             },
27628             // CDATA
27629             4: function(node) {
27630                 writer.cdata(node.nodeValue);
27631             },
27632             // Document fragment
27633             11: function(node) {
27634                 node = node.firstChild;
27635                 if (!node) {
27636                     return;
27637                 }
27638                 while(node) {
27639                     self.walk(node);
27640                     node = node.nextSibling
27641                 }
27642             }
27643         };
27644         writer.reset();
27645         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27646         return writer.getContent();
27647     },
27648
27649     walk: function(node)
27650     {
27651         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27652             handler = this.handlers[node.nodeType];
27653             
27654         if (handler) {
27655             handler(node);
27656             return;
27657         }
27658     
27659         var name = node.nodeName;
27660         var isEmpty = node.childNodes.length < 1;
27661       
27662         var writer = this.writer;
27663         var attrs = node.attributes;
27664         // Sort attributes
27665         
27666         writer.start(node.nodeName, attrs, isEmpty, node);
27667         if (isEmpty) {
27668             return;
27669         }
27670         node = node.firstChild;
27671         if (!node) {
27672             writer.end(name);
27673             return;
27674         }
27675         while (node) {
27676             this.walk(node);
27677             node = node.nextSibling;
27678         }
27679         writer.end(name);
27680         
27681     
27682     }
27683     // Serialize element and treat all non elements as fragments
27684    
27685 }; 
27686
27687 /***
27688  * This is based loosely on tinymce 
27689  * @class Roo.htmleditor.TidyWriter
27690  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27691  *
27692  * Known issues?
27693  * - not tested much with 'PRE' formated elements.
27694  * 
27695  *
27696  *
27697  */
27698
27699 Roo.htmleditor.TidyWriter = function(settings)
27700 {
27701     
27702     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27703     Roo.apply(this, settings);
27704     this.html = [];
27705     this.state = [];
27706      
27707     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27708   
27709 }
27710 Roo.htmleditor.TidyWriter.prototype = {
27711
27712  
27713     state : false,
27714     
27715     indent :  '  ',
27716     
27717     // part of state...
27718     indentstr : '',
27719     in_pre: false,
27720     in_inline : false,
27721     last_inline : false,
27722     encode : false,
27723      
27724     
27725             /**
27726     * Writes the a start element such as <p id="a">.
27727     *
27728     * @method start
27729     * @param {String} name Name of the element.
27730     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27731     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27732     */
27733     start: function(name, attrs, empty, node)
27734     {
27735         var i, l, attr, value;
27736         
27737         // there are some situations where adding line break && indentation will not work. will not work.
27738         // <span / b / i ... formating?
27739         
27740         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27741         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27742         
27743         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27744         
27745         var add_lb = name == 'BR' ? false : in_inline;
27746         
27747         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27748             i_inline = false;
27749         }
27750
27751         var indentstr =  this.indentstr;
27752         
27753         // e_inline = elements that can be inline, but still allow \n before and after?
27754         // only 'BR' ??? any others?
27755         
27756         // ADD LINE BEFORE tage
27757         if (!this.in_pre) {
27758             if (in_inline) {
27759                 //code
27760                 if (name == 'BR') {
27761                     this.addLine();
27762                 } else if (this.lastElementEndsWS()) {
27763                     this.addLine();
27764                 } else{
27765                     // otherwise - no new line. (and dont indent.)
27766                     indentstr = '';
27767                 }
27768                 
27769             } else {
27770                 this.addLine();
27771             }
27772         } else {
27773             indentstr = '';
27774         }
27775         
27776         this.html.push(indentstr + '<', name.toLowerCase());
27777         
27778         if (attrs) {
27779             for (i = 0, l = attrs.length; i < l; i++) {
27780                 attr = attrs[i];
27781                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27782             }
27783         }
27784      
27785         if (empty) {
27786             if (is_short) {
27787                 this.html[this.html.length] = '/>';
27788             } else {
27789                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27790             }
27791             var e_inline = name == 'BR' ? false : this.in_inline;
27792             
27793             if (!e_inline && !this.in_pre) {
27794                 this.addLine();
27795             }
27796             return;
27797         
27798         }
27799         // not empty..
27800         this.html[this.html.length] = '>';
27801         
27802         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27803         /*
27804         if (!in_inline && !in_pre) {
27805             var cn = node.firstChild;
27806             while(cn) {
27807                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27808                     in_inline = true
27809                     break;
27810                 }
27811                 cn = cn.nextSibling;
27812             }
27813              
27814         }
27815         */
27816         
27817         
27818         this.pushState({
27819             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27820             in_pre : in_pre,
27821             in_inline :  in_inline
27822         });
27823         // add a line after if we are not in a
27824         
27825         if (!in_inline && !in_pre) {
27826             this.addLine();
27827         }
27828         
27829             
27830          
27831         
27832     },
27833     
27834     lastElementEndsWS : function()
27835     {
27836         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27837         if (value === false) {
27838             return true;
27839         }
27840         return value.match(/\s+$/);
27841         
27842     },
27843     
27844     /**
27845      * Writes the a end element such as </p>.
27846      *
27847      * @method end
27848      * @param {String} name Name of the element.
27849      */
27850     end: function(name) {
27851         var value;
27852         this.popState();
27853         var indentstr = '';
27854         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27855         
27856         if (!this.in_pre && !in_inline) {
27857             this.addLine();
27858             indentstr  = this.indentstr;
27859         }
27860         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27861         this.last_inline = in_inline;
27862         
27863         // pop the indent state..
27864     },
27865     /**
27866      * Writes a text node.
27867      *
27868      * In pre - we should not mess with the contents.
27869      * 
27870      *
27871      * @method text
27872      * @param {String} text String to write out.
27873      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27874      */
27875     text: function(in_text, node)
27876     {
27877         // if not in whitespace critical
27878         if (in_text.length < 1) {
27879             return;
27880         }
27881         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27882         
27883         if (this.in_pre) {
27884             this.html[this.html.length] =  text;
27885             return;   
27886         }
27887         
27888         if (this.in_inline) {
27889             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27890             if (text != ' ') {
27891                 text = text.replace(/\s+/,' ');  // all white space to single white space
27892                 
27893                     
27894                 // if next tag is '<BR>', then we can trim right..
27895                 if (node.nextSibling &&
27896                     node.nextSibling.nodeType == 1 &&
27897                     node.nextSibling.nodeName == 'BR' )
27898                 {
27899                     text = text.replace(/\s+$/g,'');
27900                 }
27901                 // if previous tag was a BR, we can also trim..
27902                 if (node.previousSibling &&
27903                     node.previousSibling.nodeType == 1 &&
27904                     node.previousSibling.nodeName == 'BR' )
27905                 {
27906                     text = this.indentstr +  text.replace(/^\s+/g,'');
27907                 }
27908                 if (text.match(/\n/)) {
27909                     text = text.replace(
27910                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27911                     );
27912                     // remoeve the last whitespace / line break.
27913                     text = text.replace(/\n\s+$/,'');
27914                 }
27915                 // repace long lines
27916                 
27917             }
27918              
27919             this.html[this.html.length] =  text;
27920             return;   
27921         }
27922         // see if previous element was a inline element.
27923         var indentstr = this.indentstr;
27924    
27925         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27926         
27927         // should trim left?
27928         if (node.previousSibling &&
27929             node.previousSibling.nodeType == 1 &&
27930             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27931         {
27932             indentstr = '';
27933             
27934         } else {
27935             this.addLine();
27936             text = text.replace(/^\s+/,''); // trim left
27937           
27938         }
27939         // should trim right?
27940         if (node.nextSibling &&
27941             node.nextSibling.nodeType == 1 &&
27942             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27943         {
27944           // noop
27945             
27946         }  else {
27947             text = text.replace(/\s+$/,''); // trim right
27948         }
27949          
27950               
27951         
27952         
27953         
27954         if (text.length < 1) {
27955             return;
27956         }
27957         if (!text.match(/\n/)) {
27958             this.html.push(indentstr + text);
27959             return;
27960         }
27961         
27962         text = this.indentstr + text.replace(
27963             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27964         );
27965         // remoeve the last whitespace / line break.
27966         text = text.replace(/\s+$/,''); 
27967         
27968         this.html.push(text);
27969         
27970         // split and indent..
27971         
27972         
27973     },
27974     /**
27975      * Writes a cdata node such as <![CDATA[data]]>.
27976      *
27977      * @method cdata
27978      * @param {String} text String to write out inside the cdata.
27979      */
27980     cdata: function(text) {
27981         this.html.push('<![CDATA[', text, ']]>');
27982     },
27983     /**
27984     * Writes a comment node such as <!-- Comment -->.
27985     *
27986     * @method cdata
27987     * @param {String} text String to write out inside the comment.
27988     */
27989    comment: function(text) {
27990        this.html.push('<!--', text, '-->');
27991    },
27992     /**
27993      * Writes a PI node such as <?xml attr="value" ?>.
27994      *
27995      * @method pi
27996      * @param {String} name Name of the pi.
27997      * @param {String} text String to write out inside the pi.
27998      */
27999     pi: function(name, text) {
28000         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
28001         this.indent != '' && this.html.push('\n');
28002     },
28003     /**
28004      * Writes a doctype node such as <!DOCTYPE data>.
28005      *
28006      * @method doctype
28007      * @param {String} text String to write out inside the doctype.
28008      */
28009     doctype: function(text) {
28010         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
28011     },
28012     /**
28013      * Resets the internal buffer if one wants to reuse the writer.
28014      *
28015      * @method reset
28016      */
28017     reset: function() {
28018         this.html.length = 0;
28019         this.state = [];
28020         this.pushState({
28021             indentstr : '',
28022             in_pre : false, 
28023             in_inline : false
28024         })
28025     },
28026     /**
28027      * Returns the contents that got serialized.
28028      *
28029      * @method getContent
28030      * @return {String} HTML contents that got written down.
28031      */
28032     getContent: function() {
28033         return this.html.join('').replace(/\n$/, '');
28034     },
28035     
28036     pushState : function(cfg)
28037     {
28038         this.state.push(cfg);
28039         Roo.apply(this, cfg);
28040     },
28041     
28042     popState : function()
28043     {
28044         if (this.state.length < 1) {
28045             return; // nothing to push
28046         }
28047         var cfg = {
28048             in_pre: false,
28049             indentstr : ''
28050         };
28051         this.state.pop();
28052         if (this.state.length > 0) {
28053             cfg = this.state[this.state.length-1]; 
28054         }
28055         Roo.apply(this, cfg);
28056     },
28057     
28058     addLine: function()
28059     {
28060         if (this.html.length < 1) {
28061             return;
28062         }
28063         
28064         
28065         var value = this.html[this.html.length - 1];
28066         if (value.length > 0 && '\n' !== value) {
28067             this.html.push('\n');
28068         }
28069     }
28070     
28071     
28072 //'pre script noscript style textarea video audio iframe object code'
28073 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
28074 // inline 
28075 };
28076
28077 Roo.htmleditor.TidyWriter.inline_elements = [
28078         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28079         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28080 ];
28081 Roo.htmleditor.TidyWriter.shortend_elements = [
28082     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28083     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28084 ];
28085
28086 Roo.htmleditor.TidyWriter.whitespace_elements = [
28087     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28088 ];/***
28089  * This is based loosely on tinymce 
28090  * @class Roo.htmleditor.TidyEntities
28091  * @static
28092  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28093  *
28094  * Not 100% sure this is actually used or needed.
28095  */
28096
28097 Roo.htmleditor.TidyEntities = {
28098     
28099     /**
28100      * initialize data..
28101      */
28102     init : function (){
28103      
28104         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28105        
28106     },
28107
28108
28109     buildEntitiesLookup: function(items, radix) {
28110         var i, chr, entity, lookup = {};
28111         if (!items) {
28112             return {};
28113         }
28114         items = typeof(items) == 'string' ? items.split(',') : items;
28115         radix = radix || 10;
28116         // Build entities lookup table
28117         for (i = 0; i < items.length; i += 2) {
28118             chr = String.fromCharCode(parseInt(items[i], radix));
28119             // Only add non base entities
28120             if (!this.baseEntities[chr]) {
28121                 entity = '&' + items[i + 1] + ';';
28122                 lookup[chr] = entity;
28123                 lookup[entity] = chr;
28124             }
28125         }
28126         return lookup;
28127         
28128     },
28129     
28130     asciiMap : {
28131             128: '€',
28132             130: '‚',
28133             131: 'ƒ',
28134             132: '„',
28135             133: '…',
28136             134: '†',
28137             135: '‡',
28138             136: 'ˆ',
28139             137: '‰',
28140             138: 'Š',
28141             139: '‹',
28142             140: 'Œ',
28143             142: 'Ž',
28144             145: '‘',
28145             146: '’',
28146             147: '“',
28147             148: '”',
28148             149: '•',
28149             150: '–',
28150             151: '—',
28151             152: '˜',
28152             153: '™',
28153             154: 'š',
28154             155: '›',
28155             156: 'œ',
28156             158: 'ž',
28157             159: 'Ÿ'
28158     },
28159     // Raw entities
28160     baseEntities : {
28161         '"': '&quot;',
28162         // Needs to be escaped since the YUI compressor would otherwise break the code
28163         '\'': '&#39;',
28164         '<': '&lt;',
28165         '>': '&gt;',
28166         '&': '&amp;',
28167         '`': '&#96;'
28168     },
28169     // Reverse lookup table for raw entities
28170     reverseEntities : {
28171         '&lt;': '<',
28172         '&gt;': '>',
28173         '&amp;': '&',
28174         '&quot;': '"',
28175         '&apos;': '\''
28176     },
28177     
28178     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28179     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28180     rawCharsRegExp : /[<>&\"\']/g,
28181     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28182     namedEntities  : false,
28183     namedEntitiesData : [ 
28184         '50',
28185         'nbsp',
28186         '51',
28187         'iexcl',
28188         '52',
28189         'cent',
28190         '53',
28191         'pound',
28192         '54',
28193         'curren',
28194         '55',
28195         'yen',
28196         '56',
28197         'brvbar',
28198         '57',
28199         'sect',
28200         '58',
28201         'uml',
28202         '59',
28203         'copy',
28204         '5a',
28205         'ordf',
28206         '5b',
28207         'laquo',
28208         '5c',
28209         'not',
28210         '5d',
28211         'shy',
28212         '5e',
28213         'reg',
28214         '5f',
28215         'macr',
28216         '5g',
28217         'deg',
28218         '5h',
28219         'plusmn',
28220         '5i',
28221         'sup2',
28222         '5j',
28223         'sup3',
28224         '5k',
28225         'acute',
28226         '5l',
28227         'micro',
28228         '5m',
28229         'para',
28230         '5n',
28231         'middot',
28232         '5o',
28233         'cedil',
28234         '5p',
28235         'sup1',
28236         '5q',
28237         'ordm',
28238         '5r',
28239         'raquo',
28240         '5s',
28241         'frac14',
28242         '5t',
28243         'frac12',
28244         '5u',
28245         'frac34',
28246         '5v',
28247         'iquest',
28248         '60',
28249         'Agrave',
28250         '61',
28251         'Aacute',
28252         '62',
28253         'Acirc',
28254         '63',
28255         'Atilde',
28256         '64',
28257         'Auml',
28258         '65',
28259         'Aring',
28260         '66',
28261         'AElig',
28262         '67',
28263         'Ccedil',
28264         '68',
28265         'Egrave',
28266         '69',
28267         'Eacute',
28268         '6a',
28269         'Ecirc',
28270         '6b',
28271         'Euml',
28272         '6c',
28273         'Igrave',
28274         '6d',
28275         'Iacute',
28276         '6e',
28277         'Icirc',
28278         '6f',
28279         'Iuml',
28280         '6g',
28281         'ETH',
28282         '6h',
28283         'Ntilde',
28284         '6i',
28285         'Ograve',
28286         '6j',
28287         'Oacute',
28288         '6k',
28289         'Ocirc',
28290         '6l',
28291         'Otilde',
28292         '6m',
28293         'Ouml',
28294         '6n',
28295         'times',
28296         '6o',
28297         'Oslash',
28298         '6p',
28299         'Ugrave',
28300         '6q',
28301         'Uacute',
28302         '6r',
28303         'Ucirc',
28304         '6s',
28305         'Uuml',
28306         '6t',
28307         'Yacute',
28308         '6u',
28309         'THORN',
28310         '6v',
28311         'szlig',
28312         '70',
28313         'agrave',
28314         '71',
28315         'aacute',
28316         '72',
28317         'acirc',
28318         '73',
28319         'atilde',
28320         '74',
28321         'auml',
28322         '75',
28323         'aring',
28324         '76',
28325         'aelig',
28326         '77',
28327         'ccedil',
28328         '78',
28329         'egrave',
28330         '79',
28331         'eacute',
28332         '7a',
28333         'ecirc',
28334         '7b',
28335         'euml',
28336         '7c',
28337         'igrave',
28338         '7d',
28339         'iacute',
28340         '7e',
28341         'icirc',
28342         '7f',
28343         'iuml',
28344         '7g',
28345         'eth',
28346         '7h',
28347         'ntilde',
28348         '7i',
28349         'ograve',
28350         '7j',
28351         'oacute',
28352         '7k',
28353         'ocirc',
28354         '7l',
28355         'otilde',
28356         '7m',
28357         'ouml',
28358         '7n',
28359         'divide',
28360         '7o',
28361         'oslash',
28362         '7p',
28363         'ugrave',
28364         '7q',
28365         'uacute',
28366         '7r',
28367         'ucirc',
28368         '7s',
28369         'uuml',
28370         '7t',
28371         'yacute',
28372         '7u',
28373         'thorn',
28374         '7v',
28375         'yuml',
28376         'ci',
28377         'fnof',
28378         'sh',
28379         'Alpha',
28380         'si',
28381         'Beta',
28382         'sj',
28383         'Gamma',
28384         'sk',
28385         'Delta',
28386         'sl',
28387         'Epsilon',
28388         'sm',
28389         'Zeta',
28390         'sn',
28391         'Eta',
28392         'so',
28393         'Theta',
28394         'sp',
28395         'Iota',
28396         'sq',
28397         'Kappa',
28398         'sr',
28399         'Lambda',
28400         'ss',
28401         'Mu',
28402         'st',
28403         'Nu',
28404         'su',
28405         'Xi',
28406         'sv',
28407         'Omicron',
28408         't0',
28409         'Pi',
28410         't1',
28411         'Rho',
28412         't3',
28413         'Sigma',
28414         't4',
28415         'Tau',
28416         't5',
28417         'Upsilon',
28418         't6',
28419         'Phi',
28420         't7',
28421         'Chi',
28422         't8',
28423         'Psi',
28424         't9',
28425         'Omega',
28426         'th',
28427         'alpha',
28428         'ti',
28429         'beta',
28430         'tj',
28431         'gamma',
28432         'tk',
28433         'delta',
28434         'tl',
28435         'epsilon',
28436         'tm',
28437         'zeta',
28438         'tn',
28439         'eta',
28440         'to',
28441         'theta',
28442         'tp',
28443         'iota',
28444         'tq',
28445         'kappa',
28446         'tr',
28447         'lambda',
28448         'ts',
28449         'mu',
28450         'tt',
28451         'nu',
28452         'tu',
28453         'xi',
28454         'tv',
28455         'omicron',
28456         'u0',
28457         'pi',
28458         'u1',
28459         'rho',
28460         'u2',
28461         'sigmaf',
28462         'u3',
28463         'sigma',
28464         'u4',
28465         'tau',
28466         'u5',
28467         'upsilon',
28468         'u6',
28469         'phi',
28470         'u7',
28471         'chi',
28472         'u8',
28473         'psi',
28474         'u9',
28475         'omega',
28476         'uh',
28477         'thetasym',
28478         'ui',
28479         'upsih',
28480         'um',
28481         'piv',
28482         '812',
28483         'bull',
28484         '816',
28485         'hellip',
28486         '81i',
28487         'prime',
28488         '81j',
28489         'Prime',
28490         '81u',
28491         'oline',
28492         '824',
28493         'frasl',
28494         '88o',
28495         'weierp',
28496         '88h',
28497         'image',
28498         '88s',
28499         'real',
28500         '892',
28501         'trade',
28502         '89l',
28503         'alefsym',
28504         '8cg',
28505         'larr',
28506         '8ch',
28507         'uarr',
28508         '8ci',
28509         'rarr',
28510         '8cj',
28511         'darr',
28512         '8ck',
28513         'harr',
28514         '8dl',
28515         'crarr',
28516         '8eg',
28517         'lArr',
28518         '8eh',
28519         'uArr',
28520         '8ei',
28521         'rArr',
28522         '8ej',
28523         'dArr',
28524         '8ek',
28525         'hArr',
28526         '8g0',
28527         'forall',
28528         '8g2',
28529         'part',
28530         '8g3',
28531         'exist',
28532         '8g5',
28533         'empty',
28534         '8g7',
28535         'nabla',
28536         '8g8',
28537         'isin',
28538         '8g9',
28539         'notin',
28540         '8gb',
28541         'ni',
28542         '8gf',
28543         'prod',
28544         '8gh',
28545         'sum',
28546         '8gi',
28547         'minus',
28548         '8gn',
28549         'lowast',
28550         '8gq',
28551         'radic',
28552         '8gt',
28553         'prop',
28554         '8gu',
28555         'infin',
28556         '8h0',
28557         'ang',
28558         '8h7',
28559         'and',
28560         '8h8',
28561         'or',
28562         '8h9',
28563         'cap',
28564         '8ha',
28565         'cup',
28566         '8hb',
28567         'int',
28568         '8hk',
28569         'there4',
28570         '8hs',
28571         'sim',
28572         '8i5',
28573         'cong',
28574         '8i8',
28575         'asymp',
28576         '8j0',
28577         'ne',
28578         '8j1',
28579         'equiv',
28580         '8j4',
28581         'le',
28582         '8j5',
28583         'ge',
28584         '8k2',
28585         'sub',
28586         '8k3',
28587         'sup',
28588         '8k4',
28589         'nsub',
28590         '8k6',
28591         'sube',
28592         '8k7',
28593         'supe',
28594         '8kl',
28595         'oplus',
28596         '8kn',
28597         'otimes',
28598         '8l5',
28599         'perp',
28600         '8m5',
28601         'sdot',
28602         '8o8',
28603         'lceil',
28604         '8o9',
28605         'rceil',
28606         '8oa',
28607         'lfloor',
28608         '8ob',
28609         'rfloor',
28610         '8p9',
28611         'lang',
28612         '8pa',
28613         'rang',
28614         '9ea',
28615         'loz',
28616         '9j0',
28617         'spades',
28618         '9j3',
28619         'clubs',
28620         '9j5',
28621         'hearts',
28622         '9j6',
28623         'diams',
28624         'ai',
28625         'OElig',
28626         'aj',
28627         'oelig',
28628         'b0',
28629         'Scaron',
28630         'b1',
28631         'scaron',
28632         'bo',
28633         'Yuml',
28634         'm6',
28635         'circ',
28636         'ms',
28637         'tilde',
28638         '802',
28639         'ensp',
28640         '803',
28641         'emsp',
28642         '809',
28643         'thinsp',
28644         '80c',
28645         'zwnj',
28646         '80d',
28647         'zwj',
28648         '80e',
28649         'lrm',
28650         '80f',
28651         'rlm',
28652         '80j',
28653         'ndash',
28654         '80k',
28655         'mdash',
28656         '80o',
28657         'lsquo',
28658         '80p',
28659         'rsquo',
28660         '80q',
28661         'sbquo',
28662         '80s',
28663         'ldquo',
28664         '80t',
28665         'rdquo',
28666         '80u',
28667         'bdquo',
28668         '810',
28669         'dagger',
28670         '811',
28671         'Dagger',
28672         '81g',
28673         'permil',
28674         '81p',
28675         'lsaquo',
28676         '81q',
28677         'rsaquo',
28678         '85c',
28679         'euro'
28680     ],
28681
28682          
28683     /**
28684      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28685      *
28686      * @method encodeRaw
28687      * @param {String} text Text to encode.
28688      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28689      * @return {String} Entity encoded text.
28690      */
28691     encodeRaw: function(text, attr)
28692     {
28693         var t = this;
28694         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28695             return t.baseEntities[chr] || chr;
28696         });
28697     },
28698     /**
28699      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28700      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28701      * and is exposed as the DOMUtils.encode function.
28702      *
28703      * @method encodeAllRaw
28704      * @param {String} text Text to encode.
28705      * @return {String} Entity encoded text.
28706      */
28707     encodeAllRaw: function(text) {
28708         var t = this;
28709         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28710             return t.baseEntities[chr] || chr;
28711         });
28712     },
28713     /**
28714      * Encodes the specified string using numeric entities. The core entities will be
28715      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28716      *
28717      * @method encodeNumeric
28718      * @param {String} text Text to encode.
28719      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28720      * @return {String} Entity encoded text.
28721      */
28722     encodeNumeric: function(text, attr) {
28723         var t = this;
28724         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28725             // Multi byte sequence convert it to a single entity
28726             if (chr.length > 1) {
28727                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28728             }
28729             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28730         });
28731     },
28732     /**
28733      * Encodes the specified string using named entities. The core entities will be encoded
28734      * as named ones but all non lower ascii characters will be encoded into named entities.
28735      *
28736      * @method encodeNamed
28737      * @param {String} text Text to encode.
28738      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28739      * @param {Object} entities Optional parameter with entities to use.
28740      * @return {String} Entity encoded text.
28741      */
28742     encodeNamed: function(text, attr, entities) {
28743         var t = this;
28744         entities = entities || this.namedEntities;
28745         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28746             return t.baseEntities[chr] || entities[chr] || chr;
28747         });
28748     },
28749     /**
28750      * Returns an encode function based on the name(s) and it's optional entities.
28751      *
28752      * @method getEncodeFunc
28753      * @param {String} name Comma separated list of encoders for example named,numeric.
28754      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28755      * @return {function} Encode function to be used.
28756      */
28757     getEncodeFunc: function(name, entities) {
28758         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28759         var t = this;
28760         function encodeNamedAndNumeric(text, attr) {
28761             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28762                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28763             });
28764         }
28765
28766         function encodeCustomNamed(text, attr) {
28767             return t.encodeNamed(text, attr, entities);
28768         }
28769         // Replace + with , to be compatible with previous TinyMCE versions
28770         name = this.makeMap(name.replace(/\+/g, ','));
28771         // Named and numeric encoder
28772         if (name.named && name.numeric) {
28773             return this.encodeNamedAndNumeric;
28774         }
28775         // Named encoder
28776         if (name.named) {
28777             // Custom names
28778             if (entities) {
28779                 return encodeCustomNamed;
28780             }
28781             return this.encodeNamed;
28782         }
28783         // Numeric
28784         if (name.numeric) {
28785             return this.encodeNumeric;
28786         }
28787         // Raw encoder
28788         return this.encodeRaw;
28789     },
28790     /**
28791      * Decodes the specified string, this will replace entities with raw UTF characters.
28792      *
28793      * @method decode
28794      * @param {String} text Text to entity decode.
28795      * @return {String} Entity decoded string.
28796      */
28797     decode: function(text)
28798     {
28799         var  t = this;
28800         return text.replace(this.entityRegExp, function(all, numeric) {
28801             if (numeric) {
28802                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28803                 // Support upper UTF
28804                 if (numeric > 65535) {
28805                     numeric -= 65536;
28806                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28807                 }
28808                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28809             }
28810             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28811         });
28812     },
28813     nativeDecode : function (text) {
28814         return text;
28815     },
28816     makeMap : function (items, delim, map) {
28817                 var i;
28818                 items = items || [];
28819                 delim = delim || ',';
28820                 if (typeof items == "string") {
28821                         items = items.split(delim);
28822                 }
28823                 map = map || {};
28824                 i = items.length;
28825                 while (i--) {
28826                         map[items[i]] = {};
28827                 }
28828                 return map;
28829         }
28830 };
28831     
28832     
28833     
28834 Roo.htmleditor.TidyEntities.init();
28835 /**
28836  * @class Roo.htmleditor.KeyEnter
28837  * Handle Enter press..
28838  * @cfg {Roo.HtmlEditorCore} core the editor.
28839  * @constructor
28840  * Create a new Filter.
28841  * @param {Object} config Configuration options
28842  */
28843
28844
28845
28846
28847
28848 Roo.htmleditor.KeyEnter = function(cfg) {
28849     Roo.apply(this, cfg);
28850     // this does not actually call walk as it's really just a abstract class
28851  
28852     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28853 }
28854
28855 //Roo.htmleditor.KeyEnter.i = 0;
28856
28857
28858 Roo.htmleditor.KeyEnter.prototype = {
28859     
28860     core : false,
28861     
28862     keypress : function(e)
28863     {
28864         if (e.charCode != 13 && e.charCode != 10) {
28865             Roo.log([e.charCode,e]);
28866             return true;
28867         }
28868         e.preventDefault();
28869         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28870         var doc = this.core.doc;
28871           //add a new line
28872        
28873     
28874         var sel = this.core.getSelection();
28875         var range = sel.getRangeAt(0);
28876         var n = range.commonAncestorContainer;
28877         var pc = range.closest([ 'ol', 'ul']);
28878         var pli = range.closest('li');
28879         if (!pc || e.ctrlKey) {
28880             // on it list, or ctrl pressed.
28881             if (!e.ctrlKey) {
28882                 sel.insertNode('br', 'after'); 
28883             } else {
28884                 // only do this if we have ctrl key..
28885                 var br = doc.createElement('br');
28886                 br.className = 'clear';
28887                 br.setAttribute('style', 'clear: both');
28888                 sel.insertNode(br, 'after'); 
28889             }
28890             
28891          
28892             this.core.undoManager.addEvent();
28893             this.core.fireEditorEvent(e);
28894             return false;
28895         }
28896         
28897         // deal with <li> insetion
28898         if (pli.innerText.trim() == '' &&
28899             pli.previousSibling &&
28900             pli.previousSibling.nodeName == 'LI' &&
28901             pli.previousSibling.innerText.trim() ==  '') {
28902             pli.parentNode.removeChild(pli.previousSibling);
28903             sel.cursorAfter(pc);
28904             this.core.undoManager.addEvent();
28905             this.core.fireEditorEvent(e);
28906             return false;
28907         }
28908     
28909         var li = doc.createElement('LI');
28910         li.innerHTML = '&nbsp;';
28911         if (!pli || !pli.firstSibling) {
28912             pc.appendChild(li);
28913         } else {
28914             pli.parentNode.insertBefore(li, pli.firstSibling);
28915         }
28916         sel.cursorText (li.firstChild);
28917       
28918         this.core.undoManager.addEvent();
28919         this.core.fireEditorEvent(e);
28920
28921         return false;
28922         
28923     
28924         
28925         
28926          
28927     }
28928 };
28929      
28930 /**
28931  * @class Roo.htmleditor.Block
28932  * Base class for html editor blocks - do not use it directly .. extend it..
28933  * @cfg {DomElement} node The node to apply stuff to.
28934  * @cfg {String} friendly_name the name that appears in the context bar about this block
28935  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28936  
28937  * @constructor
28938  * Create a new Filter.
28939  * @param {Object} config Configuration options
28940  */
28941
28942 Roo.htmleditor.Block  = function(cfg)
28943 {
28944     // do nothing .. should not be called really.
28945 }
28946 /**
28947  * factory method to get the block from an element (using cache if necessary)
28948  * @static
28949  * @param {HtmlElement} the dom element
28950  */
28951 Roo.htmleditor.Block.factory = function(node)
28952 {
28953     var cc = Roo.htmleditor.Block.cache;
28954     var id = Roo.get(node).id;
28955     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28956         Roo.htmleditor.Block.cache[id].readElement(node);
28957         return Roo.htmleditor.Block.cache[id];
28958     }
28959     var db  = node.getAttribute('data-block');
28960     if (!db) {
28961         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28962     }
28963     var cls = Roo.htmleditor['Block' + db];
28964     if (typeof(cls) == 'undefined') {
28965         //Roo.log(node.getAttribute('data-block'));
28966         Roo.log("OOps missing block : " + 'Block' + db);
28967         return false;
28968     }
28969     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28970     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
28971 };
28972
28973 /**
28974  * initalize all Elements from content that are 'blockable'
28975  * @static
28976  * @param the body element
28977  */
28978 Roo.htmleditor.Block.initAll = function(body, type)
28979 {
28980     if (typeof(type) == 'undefined') {
28981         var ia = Roo.htmleditor.Block.initAll;
28982         ia(body,'table');
28983         ia(body,'td');
28984         ia(body,'figure');
28985         return;
28986     }
28987     Roo.each(Roo.get(body).query(type), function(e) {
28988         Roo.htmleditor.Block.factory(e);    
28989     },this);
28990 };
28991 // question goes here... do we need to clear out this cache sometimes?
28992 // or show we make it relivant to the htmleditor.
28993 Roo.htmleditor.Block.cache = {};
28994
28995 Roo.htmleditor.Block.prototype = {
28996     
28997     node : false,
28998     
28999      // used by context menu
29000     friendly_name : 'Based Block',
29001     
29002     // text for button to delete this element
29003     deleteTitle : false,
29004     
29005     context : false,
29006     /**
29007      * Update a node with values from this object
29008      * @param {DomElement} node
29009      */
29010     updateElement : function(node)
29011     {
29012         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
29013     },
29014      /**
29015      * convert to plain HTML for calling insertAtCursor..
29016      */
29017     toHTML : function()
29018     {
29019         return Roo.DomHelper.markup(this.toObject());
29020     },
29021     /**
29022      * used by readEleemnt to extract data from a node
29023      * may need improving as it's pretty basic
29024      
29025      * @param {DomElement} node
29026      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
29027      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
29028      * @param {String} style the style property - eg. text-align
29029      */
29030     getVal : function(node, tag, attr, style)
29031     {
29032         var n = node;
29033         if (tag !== true && n.tagName != tag.toUpperCase()) {
29034             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
29035             // but kiss for now.
29036             n = node.getElementsByTagName(tag).item(0);
29037         }
29038         if (!n) {
29039             return '';
29040         }
29041         if (attr === false) {
29042             return n;
29043         }
29044         if (attr == 'html') {
29045             return n.innerHTML;
29046         }
29047         if (attr == 'style') {
29048             return n.style[style]; 
29049         }
29050         
29051         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29052             
29053     },
29054     /**
29055      * create a DomHelper friendly object - for use with 
29056      * Roo.DomHelper.markup / overwrite / etc..
29057      * (override this)
29058      */
29059     toObject : function()
29060     {
29061         return {};
29062     },
29063       /**
29064      * Read a node that has a 'data-block' property - and extract the values from it.
29065      * @param {DomElement} node - the node
29066      */
29067     readElement : function(node)
29068     {
29069         
29070     } 
29071     
29072     
29073 };
29074
29075  
29076
29077 /**
29078  * @class Roo.htmleditor.BlockFigure
29079  * Block that has an image and a figcaption
29080  * @cfg {String} image_src the url for the image
29081  * @cfg {String} align (left|right) alignment for the block default left
29082  * @cfg {String} caption the text to appear below  (and in the alt tag)
29083  * @cfg {String} caption_display (block|none) display or not the caption
29084  * @cfg {String|number} image_width the width of the image number or %?
29085  * @cfg {String|number} image_height the height of the image number or %?
29086  * 
29087  * @constructor
29088  * Create a new Filter.
29089  * @param {Object} config Configuration options
29090  */
29091
29092 Roo.htmleditor.BlockFigure = function(cfg)
29093 {
29094     if (cfg.node) {
29095         this.readElement(cfg.node);
29096         this.updateElement(cfg.node);
29097     }
29098     Roo.apply(this, cfg);
29099 }
29100 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29101  
29102     
29103     // setable values.
29104     image_src: '',
29105     align: 'center',
29106     caption : '',
29107     caption_display : 'block',
29108     width : '100%',
29109     cls : '',
29110     href: '',
29111     video_url : '',
29112     
29113     // margin: '2%', not used
29114     
29115     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
29116
29117     
29118     // used by context menu
29119     friendly_name : 'Image with caption',
29120     deleteTitle : "Delete Image and Caption",
29121     
29122     contextMenu : function(toolbar)
29123     {
29124         
29125         var block = function() {
29126             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29127         };
29128         
29129         
29130         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29131         
29132         var syncValue = toolbar.editorcore.syncValue;
29133         
29134         var fields = {};
29135         
29136         return [
29137              {
29138                 xtype : 'TextItem',
29139                 text : "Source: ",
29140                 xns : rooui.Toolbar  //Boostrap?
29141             },
29142             {
29143                 xtype : 'Button',
29144                 text: 'Change Image URL',
29145                  
29146                 listeners : {
29147                     click: function (btn, state)
29148                     {
29149                         var b = block();
29150                         
29151                         Roo.MessageBox.show({
29152                             title : "Image Source URL",
29153                             msg : "Enter the url for the image",
29154                             buttons: Roo.MessageBox.OKCANCEL,
29155                             fn: function(btn, val){
29156                                 if (btn != 'ok') {
29157                                     return;
29158                                 }
29159                                 b.image_src = val;
29160                                 b.updateElement();
29161                                 syncValue();
29162                                 toolbar.editorcore.onEditorEvent();
29163                             },
29164                             minWidth:250,
29165                             prompt:true,
29166                             //multiline: multiline,
29167                             modal : true,
29168                             value : b.image_src
29169                         });
29170                     }
29171                 },
29172                 xns : rooui.Toolbar
29173             },
29174          
29175             {
29176                 xtype : 'Button',
29177                 text: 'Change Link URL',
29178                  
29179                 listeners : {
29180                     click: function (btn, state)
29181                     {
29182                         var b = block();
29183                         
29184                         Roo.MessageBox.show({
29185                             title : "Link URL",
29186                             msg : "Enter the url for the link - leave blank to have no link",
29187                             buttons: Roo.MessageBox.OKCANCEL,
29188                             fn: function(btn, val){
29189                                 if (btn != 'ok') {
29190                                     return;
29191                                 }
29192                                 b.href = val;
29193                                 b.updateElement();
29194                                 syncValue();
29195                                 toolbar.editorcore.onEditorEvent();
29196                             },
29197                             minWidth:250,
29198                             prompt:true,
29199                             //multiline: multiline,
29200                             modal : true,
29201                             value : b.href
29202                         });
29203                     }
29204                 },
29205                 xns : rooui.Toolbar
29206             },
29207             {
29208                 xtype : 'Button',
29209                 text: 'Show Video URL',
29210                  
29211                 listeners : {
29212                     click: function (btn, state)
29213                     {
29214                         Roo.MessageBox.alert("Video URL",
29215                             block().video_url == '' ? 'This image is not linked ot a video' :
29216                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29217                     }
29218                 },
29219                 xns : rooui.Toolbar
29220             },
29221             
29222             
29223             {
29224                 xtype : 'TextItem',
29225                 text : "Width: ",
29226                 xns : rooui.Toolbar  //Boostrap?
29227             },
29228             {
29229                 xtype : 'ComboBox',
29230                 allowBlank : false,
29231                 displayField : 'val',
29232                 editable : true,
29233                 listWidth : 100,
29234                 triggerAction : 'all',
29235                 typeAhead : true,
29236                 valueField : 'val',
29237                 width : 70,
29238                 name : 'width',
29239                 listeners : {
29240                     select : function (combo, r, index)
29241                     {
29242                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29243                         var b = block();
29244                         b.width = r.get('val');
29245                         b.updateElement();
29246                         syncValue();
29247                         toolbar.editorcore.onEditorEvent();
29248                     }
29249                 },
29250                 xns : rooui.form,
29251                 store : {
29252                     xtype : 'SimpleStore',
29253                     data : [
29254                         ['100%'],
29255                         ['80%'],
29256                         ['50%'],
29257                         ['20%'],
29258                         ['10%']
29259                     ],
29260                     fields : [ 'val'],
29261                     xns : Roo.data
29262                 }
29263             },
29264             {
29265                 xtype : 'TextItem',
29266                 text : "Align: ",
29267                 xns : rooui.Toolbar  //Boostrap?
29268             },
29269             {
29270                 xtype : 'ComboBox',
29271                 allowBlank : false,
29272                 displayField : 'val',
29273                 editable : true,
29274                 listWidth : 100,
29275                 triggerAction : 'all',
29276                 typeAhead : true,
29277                 valueField : 'val',
29278                 width : 70,
29279                 name : 'align',
29280                 listeners : {
29281                     select : function (combo, r, index)
29282                     {
29283                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29284                         var b = block();
29285                         b.align = r.get('val');
29286                         b.updateElement();
29287                         syncValue();
29288                         toolbar.editorcore.onEditorEvent();
29289                     }
29290                 },
29291                 xns : rooui.form,
29292                 store : {
29293                     xtype : 'SimpleStore',
29294                     data : [
29295                         ['left'],
29296                         ['right'],
29297                         ['center']
29298                     ],
29299                     fields : [ 'val'],
29300                     xns : Roo.data
29301                 }
29302             },
29303             
29304             
29305             {
29306                 xtype : 'Button',
29307                 text: 'Hide Caption',
29308                 name : 'caption_display',
29309                 pressed : false,
29310                 enableToggle : true,
29311                 setValue : function(v) {
29312                     // this trigger toggle.
29313                      
29314                     this.setText(v ? "Hide Caption" : "Show Caption");
29315                     this.setPressed(v != 'block');
29316                 },
29317                 listeners : {
29318                     toggle: function (btn, state)
29319                     {
29320                         var b  = block();
29321                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29322                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29323                         b.updateElement();
29324                         syncValue();
29325                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29326                         toolbar.editorcore.onEditorEvent();
29327                     }
29328                 },
29329                 xns : rooui.Toolbar
29330             }
29331         ];
29332         
29333     },
29334     /**
29335      * create a DomHelper friendly object - for use with
29336      * Roo.DomHelper.markup / overwrite / etc..
29337      */
29338     toObject : function()
29339     {
29340         var d = document.createElement('div');
29341         d.innerHTML = this.caption;
29342         
29343         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
29344         
29345         var iw = this.align == 'center' ? this.width : '100%';
29346         var img =   {
29347             tag : 'img',
29348             contenteditable : 'false',
29349             src : this.image_src,
29350             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29351             style: {
29352                 width : iw,
29353                 maxWidth : iw + ' !important', // this is not getting rendered?
29354                 margin : m  
29355                 
29356             }
29357         };
29358         /*
29359         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29360                     '<a href="{2}">' + 
29361                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
29362                     '</a>' + 
29363                 '</div>',
29364         */
29365                 
29366         if (this.href.length > 0) {
29367             img = {
29368                 tag : 'a',
29369                 href: this.href,
29370                 contenteditable : 'true',
29371                 cn : [
29372                     img
29373                 ]
29374             };
29375         }
29376         
29377         
29378         if (this.video_url.length > 0) {
29379             img = {
29380                 tag : 'div',
29381                 cls : this.cls,
29382                 frameborder : 0,
29383                 allowfullscreen : true,
29384                 width : 420,  // these are for video tricks - that we replace the outer
29385                 height : 315,
29386                 src : this.video_url,
29387                 cn : [
29388                     img
29389                 ]
29390             };
29391         }
29392         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29393         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29394         
29395   
29396         var ret =   {
29397             tag: 'figure',
29398             'data-block' : 'Figure',
29399             'data-width' : this.width, 
29400             contenteditable : 'false',
29401             
29402             style : {
29403                 display: 'block',
29404                 float :  this.align ,
29405                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29406                 width : this.align == 'center' ? '100%' : this.width,
29407                 margin:  '0px',
29408                 padding: this.align == 'center' ? '0' : '0 10px' ,
29409                 textAlign : this.align   // seems to work for email..
29410                 
29411             },
29412            
29413             
29414             align : this.align,
29415             cn : [
29416                 img,
29417               
29418                 {
29419                     tag: 'figcaption',
29420                     'data-display' : this.caption_display,
29421                     style : {
29422                         textAlign : 'left',
29423                         fontSize : '16px',
29424                         lineHeight : '24px',
29425                         display : this.caption_display,
29426                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
29427                         margin: m,
29428                         width: this.align == 'center' ?  this.width : '100%' 
29429                     
29430                          
29431                     },
29432                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
29433                     cn : [
29434                         {
29435                             tag: 'div',
29436                             style  : {
29437                                 marginTop : '16px',
29438                                 textAlign : 'left'
29439                             },
29440                             align: 'left',
29441                             cn : [
29442                                 {
29443                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
29444                                     tag : 'i',
29445                                     contenteditable : true,
29446                                     html : captionhtml
29447                                 }
29448                                 
29449                             ]
29450                         }
29451                         
29452                     ]
29453                     
29454                 }
29455             ]
29456         };
29457         return ret;
29458          
29459     },
29460     
29461     readElement : function(node)
29462     {
29463         // this should not really come from the link...
29464         this.video_url = this.getVal(node, 'div', 'src');
29465         this.cls = this.getVal(node, 'div', 'class');
29466         this.href = this.getVal(node, 'a', 'href');
29467         
29468         
29469         this.image_src = this.getVal(node, 'img', 'src');
29470          
29471         this.align = this.getVal(node, 'figure', 'align');
29472         var figcaption = this.getVal(node, 'figcaption', false);
29473         if (figcaption !== '') {
29474             this.caption = this.getVal(figcaption, 'i', 'html');
29475         }
29476         
29477
29478         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29479         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29480         this.width = this.getVal(node, true, 'data-width');
29481         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29482         
29483     },
29484     removeNode : function()
29485     {
29486         return this.node;
29487     }
29488     
29489   
29490    
29491      
29492     
29493     
29494     
29495     
29496 })
29497
29498  
29499
29500 /**
29501  * @class Roo.htmleditor.BlockTable
29502  * Block that manages a table
29503  * 
29504  * @constructor
29505  * Create a new Filter.
29506  * @param {Object} config Configuration options
29507  */
29508
29509 Roo.htmleditor.BlockTable = function(cfg)
29510 {
29511     if (cfg.node) {
29512         this.readElement(cfg.node);
29513         this.updateElement(cfg.node);
29514     }
29515     Roo.apply(this, cfg);
29516     if (!cfg.node) {
29517         this.rows = [];
29518         for(var r = 0; r < this.no_row; r++) {
29519             this.rows[r] = [];
29520             for(var c = 0; c < this.no_col; c++) {
29521                 this.rows[r][c] = this.emptyCell();
29522             }
29523         }
29524     }
29525     
29526     
29527 }
29528 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29529  
29530     rows : false,
29531     no_col : 1,
29532     no_row : 1,
29533     
29534     
29535     width: '100%',
29536     
29537     // used by context menu
29538     friendly_name : 'Table',
29539     deleteTitle : 'Delete Table',
29540     // context menu is drawn once..
29541     
29542     contextMenu : function(toolbar)
29543     {
29544         
29545         var block = function() {
29546             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29547         };
29548         
29549         
29550         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29551         
29552         var syncValue = toolbar.editorcore.syncValue;
29553         
29554         var fields = {};
29555         
29556         return [
29557             {
29558                 xtype : 'TextItem',
29559                 text : "Width: ",
29560                 xns : rooui.Toolbar  //Boostrap?
29561             },
29562             {
29563                 xtype : 'ComboBox',
29564                 allowBlank : false,
29565                 displayField : 'val',
29566                 editable : true,
29567                 listWidth : 100,
29568                 triggerAction : 'all',
29569                 typeAhead : true,
29570                 valueField : 'val',
29571                 width : 100,
29572                 name : 'width',
29573                 listeners : {
29574                     select : function (combo, r, index)
29575                     {
29576                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29577                         var b = block();
29578                         b.width = r.get('val');
29579                         b.updateElement();
29580                         syncValue();
29581                         toolbar.editorcore.onEditorEvent();
29582                     }
29583                 },
29584                 xns : rooui.form,
29585                 store : {
29586                     xtype : 'SimpleStore',
29587                     data : [
29588                         ['100%'],
29589                         ['auto']
29590                     ],
29591                     fields : [ 'val'],
29592                     xns : Roo.data
29593                 }
29594             },
29595             // -------- Cols
29596             
29597             {
29598                 xtype : 'TextItem',
29599                 text : "Columns: ",
29600                 xns : rooui.Toolbar  //Boostrap?
29601             },
29602          
29603             {
29604                 xtype : 'Button',
29605                 text: '-',
29606                 listeners : {
29607                     click : function (_self, e)
29608                     {
29609                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29610                         block().removeColumn();
29611                         syncValue();
29612                         toolbar.editorcore.onEditorEvent();
29613                     }
29614                 },
29615                 xns : rooui.Toolbar
29616             },
29617             {
29618                 xtype : 'Button',
29619                 text: '+',
29620                 listeners : {
29621                     click : function (_self, e)
29622                     {
29623                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29624                         block().addColumn();
29625                         syncValue();
29626                         toolbar.editorcore.onEditorEvent();
29627                     }
29628                 },
29629                 xns : rooui.Toolbar
29630             },
29631             // -------- ROWS
29632             {
29633                 xtype : 'TextItem',
29634                 text : "Rows: ",
29635                 xns : rooui.Toolbar  //Boostrap?
29636             },
29637          
29638             {
29639                 xtype : 'Button',
29640                 text: '-',
29641                 listeners : {
29642                     click : function (_self, e)
29643                     {
29644                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29645                         block().removeRow();
29646                         syncValue();
29647                         toolbar.editorcore.onEditorEvent();
29648                     }
29649                 },
29650                 xns : rooui.Toolbar
29651             },
29652             {
29653                 xtype : 'Button',
29654                 text: '+',
29655                 listeners : {
29656                     click : function (_self, e)
29657                     {
29658                         block().addRow();
29659                         syncValue();
29660                         toolbar.editorcore.onEditorEvent();
29661                     }
29662                 },
29663                 xns : rooui.Toolbar
29664             },
29665             // -------- ROWS
29666             {
29667                 xtype : 'Button',
29668                 text: 'Reset Column Widths',
29669                 listeners : {
29670                     
29671                     click : function (_self, e)
29672                     {
29673                         block().resetWidths();
29674                         syncValue();
29675                         toolbar.editorcore.onEditorEvent();
29676                     }
29677                 },
29678                 xns : rooui.Toolbar
29679             } 
29680             
29681             
29682             
29683         ];
29684         
29685     },
29686     
29687     
29688   /**
29689      * create a DomHelper friendly object - for use with
29690      * Roo.DomHelper.markup / overwrite / etc..
29691      * ?? should it be called with option to hide all editing features?
29692      */
29693     toObject : function()
29694     {
29695         
29696         var ret = {
29697             tag : 'table',
29698             contenteditable : 'false', // this stops cell selection from picking the table.
29699             'data-block' : 'Table',
29700             style : {
29701                 width:  this.width,
29702                 border : 'solid 1px #000', // ??? hard coded?
29703                 'border-collapse' : 'collapse' 
29704             },
29705             cn : [
29706                 { tag : 'tbody' , cn : [] }
29707             ]
29708         };
29709         
29710         // do we have a head = not really 
29711         var ncols = 0;
29712         Roo.each(this.rows, function( row ) {
29713             var tr = {
29714                 tag: 'tr',
29715                 style : {
29716                     margin: '6px',
29717                     border : 'solid 1px #000',
29718                     textAlign : 'left' 
29719                 },
29720                 cn : [ ]
29721             };
29722             
29723             ret.cn[0].cn.push(tr);
29724             // does the row have any properties? ?? height?
29725             var nc = 0;
29726             Roo.each(row, function( cell ) {
29727                 
29728                 var td = {
29729                     tag : 'td',
29730                     contenteditable :  'true',
29731                     'data-block' : 'Td',
29732                     html : cell.html,
29733                     style : cell.style
29734                 };
29735                 if (cell.colspan > 1) {
29736                     td.colspan = cell.colspan ;
29737                     nc += cell.colspan;
29738                 } else {
29739                     nc++;
29740                 }
29741                 if (cell.rowspan > 1) {
29742                     td.rowspan = cell.rowspan ;
29743                 }
29744                 
29745                 
29746                 // widths ?
29747                 tr.cn.push(td);
29748                     
29749                 
29750             }, this);
29751             ncols = Math.max(nc, ncols);
29752             
29753             
29754         }, this);
29755         // add the header row..
29756         
29757         ncols++;
29758          
29759         
29760         return ret;
29761          
29762     },
29763     
29764     readElement : function(node)
29765     {
29766         node  = node ? node : this.node ;
29767         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29768         
29769         this.rows = [];
29770         this.no_row = 0;
29771         var trs = Array.from(node.rows);
29772         trs.forEach(function(tr) {
29773             var row =  [];
29774             this.rows.push(row);
29775             
29776             this.no_row++;
29777             var no_column = 0;
29778             Array.from(tr.cells).forEach(function(td) {
29779                 
29780                 var add = {
29781                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29782                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29783                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29784                     html : td.innerHTML
29785                 };
29786                 no_column += add.colspan;
29787                      
29788                 
29789                 row.push(add);
29790                 
29791                 
29792             },this);
29793             this.no_col = Math.max(this.no_col, no_column);
29794             
29795             
29796         },this);
29797         
29798         
29799     },
29800     normalizeRows: function()
29801     {
29802         var ret= [];
29803         var rid = -1;
29804         this.rows.forEach(function(row) {
29805             rid++;
29806             ret[rid] = [];
29807             row = this.normalizeRow(row);
29808             var cid = 0;
29809             row.forEach(function(c) {
29810                 while (typeof(ret[rid][cid]) != 'undefined') {
29811                     cid++;
29812                 }
29813                 if (typeof(ret[rid]) == 'undefined') {
29814                     ret[rid] = [];
29815                 }
29816                 ret[rid][cid] = c;
29817                 c.row = rid;
29818                 c.col = cid;
29819                 if (c.rowspan < 2) {
29820                     return;
29821                 }
29822                 
29823                 for(var i = 1 ;i < c.rowspan; i++) {
29824                     if (typeof(ret[rid+i]) == 'undefined') {
29825                         ret[rid+i] = [];
29826                     }
29827                     ret[rid+i][cid] = c;
29828                 }
29829             });
29830         }, this);
29831         return ret;
29832     
29833     },
29834     
29835     normalizeRow: function(row)
29836     {
29837         var ret= [];
29838         row.forEach(function(c) {
29839             if (c.colspan < 2) {
29840                 ret.push(c);
29841                 return;
29842             }
29843             for(var i =0 ;i < c.colspan; i++) {
29844                 ret.push(c);
29845             }
29846         });
29847         return ret;
29848     
29849     },
29850     
29851     deleteColumn : function(sel)
29852     {
29853         if (!sel || sel.type != 'col') {
29854             return;
29855         }
29856         if (this.no_col < 2) {
29857             return;
29858         }
29859         
29860         this.rows.forEach(function(row) {
29861             var cols = this.normalizeRow(row);
29862             var col = cols[sel.col];
29863             if (col.colspan > 1) {
29864                 col.colspan --;
29865             } else {
29866                 row.remove(col);
29867             }
29868             
29869         }, this);
29870         this.no_col--;
29871         
29872     },
29873     removeColumn : function()
29874     {
29875         this.deleteColumn({
29876             type: 'col',
29877             col : this.no_col-1
29878         });
29879         this.updateElement();
29880     },
29881     
29882      
29883     addColumn : function()
29884     {
29885         
29886         this.rows.forEach(function(row) {
29887             row.push(this.emptyCell());
29888            
29889         }, this);
29890         this.updateElement();
29891     },
29892     
29893     deleteRow : function(sel)
29894     {
29895         if (!sel || sel.type != 'row') {
29896             return;
29897         }
29898         
29899         if (this.no_row < 2) {
29900             return;
29901         }
29902         
29903         var rows = this.normalizeRows();
29904         
29905         
29906         rows[sel.row].forEach(function(col) {
29907             if (col.rowspan > 1) {
29908                 col.rowspan--;
29909             } else {
29910                 col.remove = 1; // flage it as removed.
29911             }
29912             
29913         }, this);
29914         var newrows = [];
29915         this.rows.forEach(function(row) {
29916             newrow = [];
29917             row.forEach(function(c) {
29918                 if (typeof(c.remove) == 'undefined') {
29919                     newrow.push(c);
29920                 }
29921                 
29922             });
29923             if (newrow.length > 0) {
29924                 newrows.push(row);
29925             }
29926         });
29927         this.rows =  newrows;
29928         
29929         
29930         
29931         this.no_row--;
29932         this.updateElement();
29933         
29934     },
29935     removeRow : function()
29936     {
29937         this.deleteRow({
29938             type: 'row',
29939             row : this.no_row-1
29940         });
29941         
29942     },
29943     
29944      
29945     addRow : function()
29946     {
29947         
29948         var row = [];
29949         for (var i = 0; i < this.no_col; i++ ) {
29950             
29951             row.push(this.emptyCell());
29952            
29953         }
29954         this.rows.push(row);
29955         this.updateElement();
29956         
29957     },
29958      
29959     // the default cell object... at present...
29960     emptyCell : function() {
29961         return (new Roo.htmleditor.BlockTd({})).toObject();
29962         
29963      
29964     },
29965     
29966     removeNode : function()
29967     {
29968         return this.node;
29969     },
29970     
29971     
29972     
29973     resetWidths : function()
29974     {
29975         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29976             var nn = Roo.htmleditor.Block.factory(n);
29977             nn.width = '';
29978             nn.updateElement(n);
29979         });
29980     }
29981     
29982     
29983     
29984     
29985 })
29986
29987 /**
29988  *
29989  * editing a TD?
29990  *
29991  * since selections really work on the table cell, then editing really should work from there
29992  *
29993  * The original plan was to support merging etc... - but that may not be needed yet..
29994  *
29995  * So this simple version will support:
29996  *   add/remove cols
29997  *   adjust the width +/-
29998  *   reset the width...
29999  *   
30000  *
30001  */
30002
30003
30004  
30005
30006 /**
30007  * @class Roo.htmleditor.BlockTable
30008  * Block that manages a table
30009  * 
30010  * @constructor
30011  * Create a new Filter.
30012  * @param {Object} config Configuration options
30013  */
30014
30015 Roo.htmleditor.BlockTd = function(cfg)
30016 {
30017     if (cfg.node) {
30018         this.readElement(cfg.node);
30019         this.updateElement(cfg.node);
30020     }
30021     Roo.apply(this, cfg);
30022      
30023     
30024     
30025 }
30026 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
30027  
30028     node : false,
30029     
30030     width: '',
30031     textAlign : 'left',
30032     valign : 'top',
30033     
30034     colspan : 1,
30035     rowspan : 1,
30036     
30037     
30038     // used by context menu
30039     friendly_name : 'Table Cell',
30040     deleteTitle : false, // use our customer delete
30041     
30042     // context menu is drawn once..
30043     
30044     contextMenu : function(toolbar)
30045     {
30046         
30047         var cell = function() {
30048             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30049         };
30050         
30051         var table = function() {
30052             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30053         };
30054         
30055         var lr = false;
30056         var saveSel = function()
30057         {
30058             lr = toolbar.editorcore.getSelection().getRangeAt(0);
30059         }
30060         var restoreSel = function()
30061         {
30062             if (lr) {
30063                 (function() {
30064                     toolbar.editorcore.focus();
30065                     var cr = toolbar.editorcore.getSelection();
30066                     cr.removeAllRanges();
30067                     cr.addRange(lr);
30068                     toolbar.editorcore.onEditorEvent();
30069                 }).defer(10, this);
30070                 
30071                 
30072             }
30073         }
30074         
30075         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30076         
30077         var syncValue = toolbar.editorcore.syncValue;
30078         
30079         var fields = {};
30080         
30081         return [
30082             {
30083                 xtype : 'Button',
30084                 text : 'Edit Table',
30085                 listeners : {
30086                     click : function() {
30087                         var t = toolbar.tb.selectedNode.closest('table');
30088                         toolbar.editorcore.selectNode(t);
30089                         toolbar.editorcore.onEditorEvent();                        
30090                     }
30091                 }
30092                 
30093             },
30094               
30095            
30096              
30097             {
30098                 xtype : 'TextItem',
30099                 text : "Column Width: ",
30100                  xns : rooui.Toolbar 
30101                
30102             },
30103             {
30104                 xtype : 'Button',
30105                 text: '-',
30106                 listeners : {
30107                     click : function (_self, e)
30108                     {
30109                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30110                         cell().shrinkColumn();
30111                         syncValue();
30112                          toolbar.editorcore.onEditorEvent();
30113                     }
30114                 },
30115                 xns : rooui.Toolbar
30116             },
30117             {
30118                 xtype : 'Button',
30119                 text: '+',
30120                 listeners : {
30121                     click : function (_self, e)
30122                     {
30123                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30124                         cell().growColumn();
30125                         syncValue();
30126                         toolbar.editorcore.onEditorEvent();
30127                     }
30128                 },
30129                 xns : rooui.Toolbar
30130             },
30131             
30132             {
30133                 xtype : 'TextItem',
30134                 text : "Vertical Align: ",
30135                 xns : rooui.Toolbar  //Boostrap?
30136             },
30137             {
30138                 xtype : 'ComboBox',
30139                 allowBlank : false,
30140                 displayField : 'val',
30141                 editable : true,
30142                 listWidth : 100,
30143                 triggerAction : 'all',
30144                 typeAhead : true,
30145                 valueField : 'val',
30146                 width : 100,
30147                 name : 'valign',
30148                 listeners : {
30149                     select : function (combo, r, index)
30150                     {
30151                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30152                         var b = cell();
30153                         b.valign = r.get('val');
30154                         b.updateElement();
30155                         syncValue();
30156                         toolbar.editorcore.onEditorEvent();
30157                     }
30158                 },
30159                 xns : rooui.form,
30160                 store : {
30161                     xtype : 'SimpleStore',
30162                     data : [
30163                         ['top'],
30164                         ['middle'],
30165                         ['bottom'] // there are afew more... 
30166                     ],
30167                     fields : [ 'val'],
30168                     xns : Roo.data
30169                 }
30170             },
30171             
30172             {
30173                 xtype : 'TextItem',
30174                 text : "Merge Cells: ",
30175                  xns : rooui.Toolbar 
30176                
30177             },
30178             
30179             
30180             {
30181                 xtype : 'Button',
30182                 text: 'Right',
30183                 listeners : {
30184                     click : function (_self, e)
30185                     {
30186                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30187                         cell().mergeRight();
30188                         //block().growColumn();
30189                         syncValue();
30190                         toolbar.editorcore.onEditorEvent();
30191                     }
30192                 },
30193                 xns : rooui.Toolbar
30194             },
30195              
30196             {
30197                 xtype : 'Button',
30198                 text: 'Below',
30199                 listeners : {
30200                     click : function (_self, e)
30201                     {
30202                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30203                         cell().mergeBelow();
30204                         //block().growColumn();
30205                         syncValue();
30206                         toolbar.editorcore.onEditorEvent();
30207                     }
30208                 },
30209                 xns : rooui.Toolbar
30210             },
30211             {
30212                 xtype : 'TextItem',
30213                 text : "| ",
30214                  xns : rooui.Toolbar 
30215                
30216             },
30217             
30218             {
30219                 xtype : 'Button',
30220                 text: 'Split',
30221                 listeners : {
30222                     click : function (_self, e)
30223                     {
30224                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30225                         cell().split();
30226                         syncValue();
30227                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30228                         toolbar.editorcore.onEditorEvent();
30229                                              
30230                     }
30231                 },
30232                 xns : rooui.Toolbar
30233             },
30234             {
30235                 xtype : 'Fill',
30236                 xns : rooui.Toolbar 
30237                
30238             },
30239         
30240           
30241             {
30242                 xtype : 'Button',
30243                 text: 'Delete',
30244                  
30245                 xns : rooui.Toolbar,
30246                 menu : {
30247                     xtype : 'Menu',
30248                     xns : rooui.menu,
30249                     items : [
30250                         {
30251                             xtype : 'Item',
30252                             html: 'Column',
30253                             listeners : {
30254                                 click : function (_self, e)
30255                                 {
30256                                     var t = table();
30257                                     
30258                                     cell().deleteColumn();
30259                                     syncValue();
30260                                     toolbar.editorcore.selectNode(t.node);
30261                                     toolbar.editorcore.onEditorEvent();   
30262                                 }
30263                             },
30264                             xns : rooui.menu
30265                         },
30266                         {
30267                             xtype : 'Item',
30268                             html: 'Row',
30269                             listeners : {
30270                                 click : function (_self, e)
30271                                 {
30272                                     var t = table();
30273                                     cell().deleteRow();
30274                                     syncValue();
30275                                     
30276                                     toolbar.editorcore.selectNode(t.node);
30277                                     toolbar.editorcore.onEditorEvent();   
30278                                                          
30279                                 }
30280                             },
30281                             xns : rooui.menu
30282                         },
30283                        {
30284                             xtype : 'Separator',
30285                             xns : rooui.menu
30286                         },
30287                         {
30288                             xtype : 'Item',
30289                             html: 'Table',
30290                             listeners : {
30291                                 click : function (_self, e)
30292                                 {
30293                                     var t = table();
30294                                     var nn = t.node.nextSibling || t.node.previousSibling;
30295                                     t.node.parentNode.removeChild(t.node);
30296                                     if (nn) { 
30297                                         toolbar.editorcore.selectNode(nn, true);
30298                                     }
30299                                     toolbar.editorcore.onEditorEvent();   
30300                                                          
30301                                 }
30302                             },
30303                             xns : rooui.menu
30304                         }
30305                     ]
30306                 }
30307             }
30308             
30309             // align... << fixme
30310             
30311         ];
30312         
30313     },
30314     
30315     
30316   /**
30317      * create a DomHelper friendly object - for use with
30318      * Roo.DomHelper.markup / overwrite / etc..
30319      * ?? should it be called with option to hide all editing features?
30320      */
30321  /**
30322      * create a DomHelper friendly object - for use with
30323      * Roo.DomHelper.markup / overwrite / etc..
30324      * ?? should it be called with option to hide all editing features?
30325      */
30326     toObject : function()
30327     {
30328         var ret = {
30329             tag : 'td',
30330             contenteditable : 'true', // this stops cell selection from picking the table.
30331             'data-block' : 'Td',
30332             valign : this.valign,
30333             style : {  
30334                 'text-align' :  this.textAlign,
30335                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30336                 'border-collapse' : 'collapse',
30337                 padding : '6px', // 8 for desktop / 4 for mobile
30338                 'vertical-align': this.valign
30339             },
30340             html : this.html
30341         };
30342         if (this.width != '') {
30343             ret.width = this.width;
30344             ret.style.width = this.width;
30345         }
30346         
30347         
30348         if (this.colspan > 1) {
30349             ret.colspan = this.colspan ;
30350         } 
30351         if (this.rowspan > 1) {
30352             ret.rowspan = this.rowspan ;
30353         }
30354         
30355            
30356         
30357         return ret;
30358          
30359     },
30360     
30361     readElement : function(node)
30362     {
30363         node  = node ? node : this.node ;
30364         this.width = node.style.width;
30365         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30366         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30367         this.html = node.innerHTML;
30368         if (node.style.textAlign != '') {
30369             this.textAlign = node.style.textAlign;
30370         }
30371         
30372         
30373     },
30374      
30375     // the default cell object... at present...
30376     emptyCell : function() {
30377         return {
30378             colspan :  1,
30379             rowspan :  1,
30380             textAlign : 'left',
30381             html : "&nbsp;" // is this going to be editable now?
30382         };
30383      
30384     },
30385     
30386     removeNode : function()
30387     {
30388         return this.node.closest('table');
30389          
30390     },
30391     
30392     cellData : false,
30393     
30394     colWidths : false,
30395     
30396     toTableArray  : function()
30397     {
30398         var ret = [];
30399         var tab = this.node.closest('tr').closest('table');
30400         Array.from(tab.rows).forEach(function(r, ri){
30401             ret[ri] = [];
30402         });
30403         var rn = 0;
30404         this.colWidths = [];
30405         var all_auto = true;
30406         Array.from(tab.rows).forEach(function(r, ri){
30407             
30408             var cn = 0;
30409             Array.from(r.cells).forEach(function(ce, ci){
30410                 var c =  {
30411                     cell : ce,
30412                     row : rn,
30413                     col: cn,
30414                     colspan : ce.colSpan,
30415                     rowspan : ce.rowSpan
30416                 };
30417                 if (ce.isEqualNode(this.node)) {
30418                     this.cellData = c;
30419                 }
30420                 // if we have been filled up by a row?
30421                 if (typeof(ret[rn][cn]) != 'undefined') {
30422                     while(typeof(ret[rn][cn]) != 'undefined') {
30423                         cn++;
30424                     }
30425                     c.col = cn;
30426                 }
30427                 
30428                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30429                     this.colWidths[cn] =   ce.style.width;
30430                     if (this.colWidths[cn] != '') {
30431                         all_auto = false;
30432                     }
30433                 }
30434                 
30435                 
30436                 if (c.colspan < 2 && c.rowspan < 2 ) {
30437                     ret[rn][cn] = c;
30438                     cn++;
30439                     return;
30440                 }
30441                 for(var j = 0; j < c.rowspan; j++) {
30442                     if (typeof(ret[rn+j]) == 'undefined') {
30443                         continue; // we have a problem..
30444                     }
30445                     ret[rn+j][cn] = c;
30446                     for(var i = 0; i < c.colspan; i++) {
30447                         ret[rn+j][cn+i] = c;
30448                     }
30449                 }
30450                 
30451                 cn += c.colspan;
30452             }, this);
30453             rn++;
30454         }, this);
30455         
30456         // initalize widths.?
30457         // either all widths or no widths..
30458         if (all_auto) {
30459             this.colWidths[0] = false; // no widths flag.
30460         }
30461         
30462         
30463         return ret;
30464         
30465     },
30466     
30467     
30468     
30469     
30470     mergeRight: function()
30471     {
30472          
30473         // get the contents of the next cell along..
30474         var tr = this.node.closest('tr');
30475         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30476         if (i >= tr.childNodes.length - 1) {
30477             return; // no cells on right to merge with.
30478         }
30479         var table = this.toTableArray();
30480         
30481         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30482             return; // nothing right?
30483         }
30484         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30485         // right cell - must be same rowspan and on the same row.
30486         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30487             return; // right hand side is not same rowspan.
30488         }
30489         
30490         
30491         
30492         this.node.innerHTML += ' ' + rc.cell.innerHTML;
30493         tr.removeChild(rc.cell);
30494         this.colspan += rc.colspan;
30495         this.node.setAttribute('colspan', this.colspan);
30496
30497         var table = this.toTableArray();
30498         this.normalizeWidths(table);
30499         this.updateWidths(table);
30500     },
30501     
30502     
30503     mergeBelow : function()
30504     {
30505         var table = this.toTableArray();
30506         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30507             return; // no row below
30508         }
30509         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30510             return; // nothing right?
30511         }
30512         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30513         
30514         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30515             return; // right hand side is not same rowspan.
30516         }
30517         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30518         rc.cell.parentNode.removeChild(rc.cell);
30519         this.rowspan += rc.rowspan;
30520         this.node.setAttribute('rowspan', this.rowspan);
30521     },
30522     
30523     split: function()
30524     {
30525         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30526             return;
30527         }
30528         var table = this.toTableArray();
30529         var cd = this.cellData;
30530         this.rowspan = 1;
30531         this.colspan = 1;
30532         
30533         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30534              
30535             
30536             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30537                 if (r == cd.row && c == cd.col) {
30538                     this.node.removeAttribute('rowspan');
30539                     this.node.removeAttribute('colspan');
30540                 }
30541                  
30542                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30543                 ntd.removeAttribute('id'); 
30544                 ntd.style.width  = this.colWidths[c];
30545                 ntd.innerHTML = '';
30546                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30547             }
30548             
30549         }
30550         this.redrawAllCells(table);
30551         
30552     },
30553     
30554     
30555     
30556     redrawAllCells: function(table)
30557     {
30558         
30559          
30560         var tab = this.node.closest('tr').closest('table');
30561         var ctr = tab.rows[0].parentNode;
30562         Array.from(tab.rows).forEach(function(r, ri){
30563             
30564             Array.from(r.cells).forEach(function(ce, ci){
30565                 ce.parentNode.removeChild(ce);
30566             });
30567             r.parentNode.removeChild(r);
30568         });
30569         for(var r = 0 ; r < table.length; r++) {
30570             var re = tab.rows[r];
30571             
30572             var re = tab.ownerDocument.createElement('tr');
30573             ctr.appendChild(re);
30574             for(var c = 0 ; c < table[r].length; c++) {
30575                 if (table[r][c].cell === false) {
30576                     continue;
30577                 }
30578                 
30579                 re.appendChild(table[r][c].cell);
30580                  
30581                 table[r][c].cell = false;
30582             }
30583         }
30584         
30585     },
30586     updateWidths : function(table)
30587     {
30588         for(var r = 0 ; r < table.length; r++) {
30589            
30590             for(var c = 0 ; c < table[r].length; c++) {
30591                 if (table[r][c].cell === false) {
30592                     continue;
30593                 }
30594                 
30595                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30596                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30597                     el.width = Math.floor(this.colWidths[c])  +'%';
30598                     el.updateElement(el.node);
30599                 }
30600                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30601                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30602                     var width = 0;
30603                     for(var i = 0; i < table[r][c].colspan; i ++) {
30604                         width += Math.floor(this.colWidths[c + i]);
30605                     }
30606                     el.width = width  +'%';
30607                     el.updateElement(el.node);
30608                 }
30609                 table[r][c].cell = false; // done
30610             }
30611         }
30612     },
30613     normalizeWidths : function(table)
30614     {
30615         if (this.colWidths[0] === false) {
30616             var nw = 100.0 / this.colWidths.length;
30617             this.colWidths.forEach(function(w,i) {
30618                 this.colWidths[i] = nw;
30619             },this);
30620             return;
30621         }
30622     
30623         var t = 0, missing = [];
30624         
30625         this.colWidths.forEach(function(w,i) {
30626             //if you mix % and
30627             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30628             var add =  this.colWidths[i];
30629             if (add > 0) {
30630                 t+=add;
30631                 return;
30632             }
30633             missing.push(i);
30634             
30635             
30636         },this);
30637         var nc = this.colWidths.length;
30638         if (missing.length) {
30639             var mult = (nc - missing.length) / (1.0 * nc);
30640             var t = mult * t;
30641             var ew = (100 -t) / (1.0 * missing.length);
30642             this.colWidths.forEach(function(w,i) {
30643                 if (w > 0) {
30644                     this.colWidths[i] = w * mult;
30645                     return;
30646                 }
30647                 
30648                 this.colWidths[i] = ew;
30649             }, this);
30650             // have to make up numbers..
30651              
30652         }
30653         // now we should have all the widths..
30654         
30655     
30656     },
30657     
30658     shrinkColumn : function()
30659     {
30660         var table = this.toTableArray();
30661         this.normalizeWidths(table);
30662         var col = this.cellData.col;
30663         var nw = this.colWidths[col] * 0.8;
30664         if (nw < 5) {
30665             return;
30666         }
30667         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30668         this.colWidths.forEach(function(w,i) {
30669             if (i == col) {
30670                  this.colWidths[i] = nw;
30671                 return;
30672             }
30673             this.colWidths[i] += otherAdd
30674         }, this);
30675         this.updateWidths(table);
30676          
30677     },
30678     growColumn : function()
30679     {
30680         var table = this.toTableArray();
30681         this.normalizeWidths(table);
30682         var col = this.cellData.col;
30683         var nw = this.colWidths[col] * 1.2;
30684         if (nw > 90) {
30685             return;
30686         }
30687         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30688         this.colWidths.forEach(function(w,i) {
30689             if (i == col) {
30690                 this.colWidths[i] = nw;
30691                 return;
30692             }
30693             this.colWidths[i] -= otherSub
30694         }, this);
30695         this.updateWidths(table);
30696          
30697     },
30698     deleteRow : function()
30699     {
30700         // delete this rows 'tr'
30701         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30702         // then reduce the rowspan.
30703         var table = this.toTableArray();
30704         // this.cellData.row;
30705         for (var i =0;i< table[this.cellData.row].length ; i++) {
30706             var c = table[this.cellData.row][i];
30707             if (c.row != this.cellData.row) {
30708                 
30709                 c.rowspan--;
30710                 c.cell.setAttribute('rowspan', c.rowspan);
30711                 continue;
30712             }
30713             if (c.rowspan > 1) {
30714                 c.rowspan--;
30715                 c.cell.setAttribute('rowspan', c.rowspan);
30716             }
30717         }
30718         table.splice(this.cellData.row,1);
30719         this.redrawAllCells(table);
30720         
30721     },
30722     deleteColumn : function()
30723     {
30724         var table = this.toTableArray();
30725         
30726         for (var i =0;i< table.length ; i++) {
30727             var c = table[i][this.cellData.col];
30728             if (c.col != this.cellData.col) {
30729                 table[i][this.cellData.col].colspan--;
30730             } else if (c.colspan > 1) {
30731                 c.colspan--;
30732                 c.cell.setAttribute('colspan', c.colspan);
30733             }
30734             table[i].splice(this.cellData.col,1);
30735         }
30736         
30737         this.redrawAllCells(table);
30738     }
30739     
30740     
30741     
30742     
30743 })
30744
30745 //<script type="text/javascript">
30746
30747 /*
30748  * Based  Ext JS Library 1.1.1
30749  * Copyright(c) 2006-2007, Ext JS, LLC.
30750  * LGPL
30751  *
30752  */
30753  
30754 /**
30755  * @class Roo.HtmlEditorCore
30756  * @extends Roo.Component
30757  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30758  *
30759  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30760  */
30761
30762 Roo.HtmlEditorCore = function(config){
30763     
30764     
30765     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30766     
30767     
30768     this.addEvents({
30769         /**
30770          * @event initialize
30771          * Fires when the editor is fully initialized (including the iframe)
30772          * @param {Roo.HtmlEditorCore} this
30773          */
30774         initialize: true,
30775         /**
30776          * @event activate
30777          * Fires when the editor is first receives the focus. Any insertion must wait
30778          * until after this event.
30779          * @param {Roo.HtmlEditorCore} this
30780          */
30781         activate: true,
30782          /**
30783          * @event beforesync
30784          * Fires before the textarea is updated with content from the editor iframe. Return false
30785          * to cancel the sync.
30786          * @param {Roo.HtmlEditorCore} this
30787          * @param {String} html
30788          */
30789         beforesync: true,
30790          /**
30791          * @event beforepush
30792          * Fires before the iframe editor is updated with content from the textarea. Return false
30793          * to cancel the push.
30794          * @param {Roo.HtmlEditorCore} this
30795          * @param {String} html
30796          */
30797         beforepush: true,
30798          /**
30799          * @event sync
30800          * Fires when the textarea is updated with content from the editor iframe.
30801          * @param {Roo.HtmlEditorCore} this
30802          * @param {String} html
30803          */
30804         sync: true,
30805          /**
30806          * @event push
30807          * Fires when the iframe editor is updated with content from the textarea.
30808          * @param {Roo.HtmlEditorCore} this
30809          * @param {String} html
30810          */
30811         push: true,
30812         
30813         /**
30814          * @event editorevent
30815          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30816          * @param {Roo.HtmlEditorCore} this
30817          */
30818         editorevent: true 
30819          
30820         
30821     });
30822     
30823     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30824     
30825     // defaults : white / black...
30826     this.applyBlacklists();
30827     
30828     
30829     
30830 };
30831
30832
30833 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30834
30835
30836      /**
30837      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30838      */
30839     
30840     owner : false,
30841     
30842      /**
30843      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30844      *                        Roo.resizable.
30845      */
30846     resizable : false,
30847      /**
30848      * @cfg {Number} height (in pixels)
30849      */   
30850     height: 300,
30851    /**
30852      * @cfg {Number} width (in pixels)
30853      */   
30854     width: 500,
30855      /**
30856      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30857      *         if you are doing an email editor, this probably needs disabling, it's designed
30858      */
30859     autoClean: true,
30860     
30861     /**
30862      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30863      */
30864     enableBlocks : true,
30865     /**
30866      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30867      * 
30868      */
30869     stylesheets: false,
30870      /**
30871      * @cfg {String} language default en - language of text (usefull for rtl languages)
30872      * 
30873      */
30874     language: 'en',
30875     
30876     /**
30877      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30878      *          - by default they are stripped - if you are editing email you may need this.
30879      */
30880     allowComments: false,
30881     // id of frame..
30882     frameId: false,
30883     
30884     // private properties
30885     validationEvent : false,
30886     deferHeight: true,
30887     initialized : false,
30888     activated : false,
30889     sourceEditMode : false,
30890     onFocus : Roo.emptyFn,
30891     iframePad:3,
30892     hideMode:'offsets',
30893     
30894     clearUp: true,
30895     
30896     // blacklist + whitelisted elements..
30897     black: false,
30898     white: false,
30899      
30900     bodyCls : '',
30901
30902     
30903     undoManager : false,
30904     /**
30905      * Protected method that will not generally be called directly. It
30906      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30907      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30908      */
30909     getDocMarkup : function(){
30910         // body styles..
30911         var st = '';
30912         
30913         // inherit styels from page...?? 
30914         if (this.stylesheets === false) {
30915             
30916             Roo.get(document.head).select('style').each(function(node) {
30917                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30918             });
30919             
30920             Roo.get(document.head).select('link').each(function(node) { 
30921                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30922             });
30923             
30924         } else if (!this.stylesheets.length) {
30925                 // simple..
30926                 st = '<style type="text/css">' +
30927                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30928                    '</style>';
30929         } else {
30930             for (var i in this.stylesheets) {
30931                 if (typeof(this.stylesheets[i]) != 'string') {
30932                     continue;
30933                 }
30934                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30935             }
30936             
30937         }
30938         
30939         st +=  '<style type="text/css">' +
30940             'IMG { cursor: pointer } ' +
30941         '</style>';
30942         
30943         st += '<meta name="google" content="notranslate">';
30944         
30945         var cls = 'notranslate roo-htmleditor-body';
30946         
30947         if(this.bodyCls.length){
30948             cls += ' ' + this.bodyCls;
30949         }
30950         
30951         return '<html  class="notranslate" translate="no"><head>' + st  +
30952             //<style type="text/css">' +
30953             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30954             //'</style>' +
30955             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30956     },
30957
30958     // private
30959     onRender : function(ct, position)
30960     {
30961         var _t = this;
30962         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30963         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30964         
30965         
30966         this.el.dom.style.border = '0 none';
30967         this.el.dom.setAttribute('tabIndex', -1);
30968         this.el.addClass('x-hidden hide');
30969         
30970         
30971         
30972         if(Roo.isIE){ // fix IE 1px bogus margin
30973             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30974         }
30975        
30976         
30977         this.frameId = Roo.id();
30978         
30979          
30980         
30981         var iframe = this.owner.wrap.createChild({
30982             tag: 'iframe',
30983             cls: 'form-control', // bootstrap..
30984             id: this.frameId,
30985             name: this.frameId,
30986             frameBorder : 'no',
30987             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
30988         }, this.el
30989         );
30990         
30991         
30992         this.iframe = iframe.dom;
30993
30994         this.assignDocWin();
30995         
30996         this.doc.designMode = 'on';
30997        
30998         this.doc.open();
30999         this.doc.write(this.getDocMarkup());
31000         this.doc.close();
31001
31002         
31003         var task = { // must defer to wait for browser to be ready
31004             run : function(){
31005                 //console.log("run task?" + this.doc.readyState);
31006                 this.assignDocWin();
31007                 if(this.doc.body || this.doc.readyState == 'complete'){
31008                     try {
31009                         this.doc.designMode="on";
31010                         
31011                     } catch (e) {
31012                         return;
31013                     }
31014                     Roo.TaskMgr.stop(task);
31015                     this.initEditor.defer(10, this);
31016                 }
31017             },
31018             interval : 10,
31019             duration: 10000,
31020             scope: this
31021         };
31022         Roo.TaskMgr.start(task);
31023
31024     },
31025
31026     // private
31027     onResize : function(w, h)
31028     {
31029          Roo.log('resize: ' +w + ',' + h );
31030         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31031         if(!this.iframe){
31032             return;
31033         }
31034         if(typeof w == 'number'){
31035             
31036             this.iframe.style.width = w + 'px';
31037         }
31038         if(typeof h == 'number'){
31039             
31040             this.iframe.style.height = h + 'px';
31041             if(this.doc){
31042                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31043             }
31044         }
31045         
31046     },
31047
31048     /**
31049      * Toggles the editor between standard and source edit mode.
31050      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31051      */
31052     toggleSourceEdit : function(sourceEditMode){
31053         
31054         this.sourceEditMode = sourceEditMode === true;
31055         
31056         if(this.sourceEditMode){
31057  
31058             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
31059             
31060         }else{
31061             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31062             //this.iframe.className = '';
31063             this.deferFocus();
31064         }
31065         //this.setSize(this.owner.wrap.getSize());
31066         //this.fireEvent('editmodechange', this, this.sourceEditMode);
31067     },
31068
31069     
31070   
31071
31072     /**
31073      * Protected method that will not generally be called directly. If you need/want
31074      * custom HTML cleanup, this is the method you should override.
31075      * @param {String} html The HTML to be cleaned
31076      * return {String} The cleaned HTML
31077      */
31078     cleanHtml : function(html)
31079     {
31080         html = String(html);
31081         if(html.length > 5){
31082             if(Roo.isSafari){ // strip safari nonsense
31083                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31084             }
31085         }
31086         if(html == '&nbsp;'){
31087             html = '';
31088         }
31089         return html;
31090     },
31091
31092     /**
31093      * HTML Editor -> Textarea
31094      * Protected method that will not generally be called directly. Syncs the contents
31095      * of the editor iframe with the textarea.
31096      */
31097     syncValue : function()
31098     {
31099         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31100         if(this.initialized){
31101             
31102             if (this.undoManager) {
31103                 this.undoManager.addEvent();
31104             }
31105
31106             
31107             var bd = (this.doc.body || this.doc.documentElement);
31108            
31109             
31110             var sel = this.win.getSelection();
31111             
31112             var div = document.createElement('div');
31113             div.innerHTML = bd.innerHTML;
31114             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31115             if (gtx.length > 0) {
31116                 var rm = gtx.item(0).parentNode;
31117                 rm.parentNode.removeChild(rm);
31118             }
31119             
31120            
31121             if (this.enableBlocks) {
31122                 new Roo.htmleditor.FilterBlock({ node : div });
31123             }
31124             
31125             var html = div.innerHTML;
31126             
31127             //?? tidy?
31128             if (this.autoClean) {
31129                 
31130                 new Roo.htmleditor.FilterAttributes({
31131                     node : div,
31132                     attrib_white : [
31133                             'href',
31134                             'src',
31135                             'name',
31136                             'align',
31137                             'colspan',
31138                             'rowspan',
31139                             'data-display',
31140                             'data-width',
31141                             'start' ,
31142                             'style',
31143                             // youtube embed.
31144                             'class',
31145                             'allowfullscreen',
31146                             'frameborder',
31147                             'width',
31148                             'height',
31149                             'alt'
31150                             ],
31151                     attrib_clean : ['href', 'src' ] 
31152                 });
31153                 
31154                 var tidy = new Roo.htmleditor.TidySerializer({
31155                     inner:  true
31156                 });
31157                 html  = tidy.serialize(div);
31158                 
31159             }
31160             
31161             
31162             if(Roo.isSafari){
31163                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31164                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31165                 if(m && m[1]){
31166                     html = '<div style="'+m[0]+'">' + html + '</div>';
31167                 }
31168             }
31169             html = this.cleanHtml(html);
31170             // fix up the special chars.. normaly like back quotes in word...
31171             // however we do not want to do this with chinese..
31172             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31173                 
31174                 var cc = match.charCodeAt();
31175
31176                 // Get the character value, handling surrogate pairs
31177                 if (match.length == 2) {
31178                     // It's a surrogate pair, calculate the Unicode code point
31179                     var high = match.charCodeAt(0) - 0xD800;
31180                     var low  = match.charCodeAt(1) - 0xDC00;
31181                     cc = (high * 0x400) + low + 0x10000;
31182                 }  else if (
31183                     (cc >= 0x4E00 && cc < 0xA000 ) ||
31184                     (cc >= 0x3400 && cc < 0x4E00 ) ||
31185                     (cc >= 0xf900 && cc < 0xfb00 )
31186                 ) {
31187                         return match;
31188                 }  
31189          
31190                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31191                 return "&#" + cc + ";";
31192                 
31193                 
31194             });
31195             
31196             
31197              
31198             if(this.owner.fireEvent('beforesync', this, html) !== false){
31199                 this.el.dom.value = html;
31200                 this.owner.fireEvent('sync', this, html);
31201             }
31202         }
31203     },
31204
31205     /**
31206      * TEXTAREA -> EDITABLE
31207      * Protected method that will not generally be called directly. Pushes the value of the textarea
31208      * into the iframe editor.
31209      */
31210     pushValue : function()
31211     {
31212         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31213         if(this.initialized){
31214             var v = this.el.dom.value.trim();
31215             
31216             
31217             if(this.owner.fireEvent('beforepush', this, v) !== false){
31218                 var d = (this.doc.body || this.doc.documentElement);
31219                 d.innerHTML = v;
31220                  
31221                 this.el.dom.value = d.innerHTML;
31222                 this.owner.fireEvent('push', this, v);
31223             }
31224             if (this.autoClean) {
31225                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31226                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31227             }
31228             if (this.enableBlocks) {
31229                 Roo.htmleditor.Block.initAll(this.doc.body);
31230             }
31231             
31232             this.updateLanguage();
31233             
31234             var lc = this.doc.body.lastChild;
31235             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31236                 // add an extra line at the end.
31237                 this.doc.body.appendChild(this.doc.createElement('br'));
31238             }
31239             
31240             
31241         }
31242     },
31243
31244     // private
31245     deferFocus : function(){
31246         this.focus.defer(10, this);
31247     },
31248
31249     // doc'ed in Field
31250     focus : function(){
31251         if(this.win && !this.sourceEditMode){
31252             this.win.focus();
31253         }else{
31254             this.el.focus();
31255         }
31256     },
31257     
31258     assignDocWin: function()
31259     {
31260         var iframe = this.iframe;
31261         
31262          if(Roo.isIE){
31263             this.doc = iframe.contentWindow.document;
31264             this.win = iframe.contentWindow;
31265         } else {
31266 //            if (!Roo.get(this.frameId)) {
31267 //                return;
31268 //            }
31269 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31270 //            this.win = Roo.get(this.frameId).dom.contentWindow;
31271             
31272             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31273                 return;
31274             }
31275             
31276             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31277             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31278         }
31279     },
31280     
31281     // private
31282     initEditor : function(){
31283         //console.log("INIT EDITOR");
31284         this.assignDocWin();
31285         
31286         
31287         
31288         this.doc.designMode="on";
31289         this.doc.open();
31290         this.doc.write(this.getDocMarkup());
31291         this.doc.close();
31292         
31293         var dbody = (this.doc.body || this.doc.documentElement);
31294         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31295         // this copies styles from the containing element into thsi one..
31296         // not sure why we need all of this..
31297         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31298         
31299         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31300         //ss['background-attachment'] = 'fixed'; // w3c
31301         dbody.bgProperties = 'fixed'; // ie
31302         dbody.setAttribute("translate", "no");
31303         
31304         //Roo.DomHelper.applyStyles(dbody, ss);
31305         Roo.EventManager.on(this.doc, {
31306              
31307             'mouseup': this.onEditorEvent,
31308             'dblclick': this.onEditorEvent,
31309             'click': this.onEditorEvent,
31310             'keyup': this.onEditorEvent,
31311             
31312             buffer:100,
31313             scope: this
31314         });
31315         Roo.EventManager.on(this.doc, {
31316             'paste': this.onPasteEvent,
31317             scope : this
31318         });
31319         if(Roo.isGecko){
31320             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31321         }
31322         //??? needed???
31323         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31324             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31325         }
31326         this.initialized = true;
31327
31328         
31329         // initialize special key events - enter
31330         new Roo.htmleditor.KeyEnter({core : this});
31331         
31332          
31333         
31334         this.owner.fireEvent('initialize', this);
31335         this.pushValue();
31336     },
31337     // this is to prevent a href clicks resulting in a redirect?
31338    
31339     onPasteEvent : function(e,v)
31340     {
31341         // I think we better assume paste is going to be a dirty load of rubish from word..
31342         
31343         // even pasting into a 'email version' of this widget will have to clean up that mess.
31344         var cd = (e.browserEvent.clipboardData || window.clipboardData);
31345         
31346         // check what type of paste - if it's an image, then handle it differently.
31347         if (cd.files && cd.files.length > 0) {
31348             // pasting images?
31349             var urlAPI = (window.createObjectURL && window) || 
31350                 (window.URL && URL.revokeObjectURL && URL) || 
31351                 (window.webkitURL && webkitURL);
31352     
31353             var url = urlAPI.createObjectURL( cd.files[0]);
31354             this.insertAtCursor('<img src=" + url + ">');
31355             return false;
31356         }
31357         if (cd.types.indexOf('text/html') < 0 ) {
31358             return false;
31359         }
31360         var images = [];
31361         var html = cd.getData('text/html'); // clipboard event
31362         if (cd.types.indexOf('text/rtf') > -1) {
31363             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31364             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31365         }
31366         //Roo.log(images);
31367         //Roo.log(imgs);
31368         // fixme..
31369         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31370                        .map(function(g) { return g.toDataURL(); })
31371                        .filter(function(g) { return g != 'about:blank'; });
31372         
31373         //Roo.log(html);
31374         html = this.cleanWordChars(html);
31375         
31376         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31377         
31378         
31379         var sn = this.getParentElement();
31380         // check if d contains a table, and prevent nesting??
31381         //Roo.log(d.getElementsByTagName('table'));
31382         //Roo.log(sn);
31383         //Roo.log(sn.closest('table'));
31384         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31385             e.preventDefault();
31386             this.insertAtCursor("You can not nest tables");
31387             //Roo.log("prevent?"); // fixme - 
31388             return false;
31389         }
31390         
31391         
31392         
31393         if (images.length > 0) {
31394             // replace all v:imagedata - with img.
31395             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31396             Roo.each(ar, function(node) {
31397                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31398                 node.parentNode.removeChild(node);
31399             });
31400             
31401             
31402             Roo.each(d.getElementsByTagName('img'), function(img, i) {
31403                 img.setAttribute('src', images[i]);
31404             });
31405         }
31406         if (this.autoClean) {
31407             new Roo.htmleditor.FilterWord({ node : d });
31408             
31409             new Roo.htmleditor.FilterStyleToTag({ node : d });
31410             new Roo.htmleditor.FilterAttributes({
31411                 node : d,
31412                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31413                 attrib_clean : ['href', 'src' ] 
31414             });
31415             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31416             // should be fonts..
31417             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31418             new Roo.htmleditor.FilterParagraph({ node : d });
31419             new Roo.htmleditor.FilterSpan({ node : d });
31420             new Roo.htmleditor.FilterLongBr({ node : d });
31421             new Roo.htmleditor.FilterComment({ node : d });
31422             
31423             
31424         }
31425         if (this.enableBlocks) {
31426                 
31427             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31428                 if (img.closest('figure')) { // assume!! that it's aready
31429                     return;
31430                 }
31431                 var fig  = new Roo.htmleditor.BlockFigure({
31432                     image_src  : img.src
31433                 });
31434                 fig.updateElement(img); // replace it..
31435                 
31436             });
31437         }
31438         
31439         
31440         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31441         if (this.enableBlocks) {
31442             Roo.htmleditor.Block.initAll(this.doc.body);
31443         }
31444          
31445         
31446         e.preventDefault();
31447         this.owner.fireEvent('paste', this);
31448         return false;
31449         // default behaveiour should be our local cleanup paste? (optional?)
31450         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31451         //this.owner.fireEvent('paste', e, v);
31452     },
31453     // private
31454     onDestroy : function(){
31455         
31456         
31457         
31458         if(this.rendered){
31459             
31460             //for (var i =0; i < this.toolbars.length;i++) {
31461             //    // fixme - ask toolbars for heights?
31462             //    this.toolbars[i].onDestroy();
31463            // }
31464             
31465             //this.wrap.dom.innerHTML = '';
31466             //this.wrap.remove();
31467         }
31468     },
31469
31470     // private
31471     onFirstFocus : function(){
31472         
31473         this.assignDocWin();
31474         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31475         
31476         this.activated = true;
31477          
31478     
31479         if(Roo.isGecko){ // prevent silly gecko errors
31480             this.win.focus();
31481             var s = this.win.getSelection();
31482             if(!s.focusNode || s.focusNode.nodeType != 3){
31483                 var r = s.getRangeAt(0);
31484                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31485                 r.collapse(true);
31486                 this.deferFocus();
31487             }
31488             try{
31489                 this.execCmd('useCSS', true);
31490                 this.execCmd('styleWithCSS', false);
31491             }catch(e){}
31492         }
31493         this.owner.fireEvent('activate', this);
31494     },
31495
31496     // private
31497     adjustFont: function(btn){
31498         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31499         //if(Roo.isSafari){ // safari
31500         //    adjust *= 2;
31501        // }
31502         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31503         if(Roo.isSafari){ // safari
31504             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31505             v =  (v < 10) ? 10 : v;
31506             v =  (v > 48) ? 48 : v;
31507             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31508             
31509         }
31510         
31511         
31512         v = Math.max(1, v+adjust);
31513         
31514         this.execCmd('FontSize', v  );
31515     },
31516
31517     onEditorEvent : function(e)
31518     {
31519          
31520         
31521         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31522             return; // we do not handle this.. (undo manager does..)
31523         }
31524         // in theory this detects if the last element is not a br, then we try and do that.
31525         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31526         if (e &&
31527             e.target.nodeName == 'BODY' &&
31528             e.type == "mouseup" &&
31529             this.doc.body.lastChild
31530            ) {
31531             var lc = this.doc.body.lastChild;
31532             // gtx-trans is google translate plugin adding crap.
31533             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31534                 lc = lc.previousSibling;
31535             }
31536             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31537             // if last element is <BR> - then dont do anything.
31538             
31539                 var ns = this.doc.createElement('br');
31540                 this.doc.body.appendChild(ns);
31541                 range = this.doc.createRange();
31542                 range.setStartAfter(ns);
31543                 range.collapse(true);
31544                 var sel = this.win.getSelection();
31545                 sel.removeAllRanges();
31546                 sel.addRange(range);
31547             }
31548         }
31549         
31550         
31551         
31552         this.fireEditorEvent(e);
31553       //  this.updateToolbar();
31554         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31555     },
31556     
31557     fireEditorEvent: function(e)
31558     {
31559         this.owner.fireEvent('editorevent', this, e);
31560     },
31561
31562     insertTag : function(tg)
31563     {
31564         // could be a bit smarter... -> wrap the current selected tRoo..
31565         if (tg.toLowerCase() == 'span' ||
31566             tg.toLowerCase() == 'code' ||
31567             tg.toLowerCase() == 'sup' ||
31568             tg.toLowerCase() == 'sub' 
31569             ) {
31570             
31571             range = this.createRange(this.getSelection());
31572             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31573             wrappingNode.appendChild(range.extractContents());
31574             range.insertNode(wrappingNode);
31575
31576             return;
31577             
31578             
31579             
31580         }
31581         this.execCmd("formatblock",   tg);
31582         this.undoManager.addEvent(); 
31583     },
31584     
31585     insertText : function(txt)
31586     {
31587         
31588         
31589         var range = this.createRange();
31590         range.deleteContents();
31591                //alert(Sender.getAttribute('label'));
31592                
31593         range.insertNode(this.doc.createTextNode(txt));
31594         this.undoManager.addEvent();
31595     } ,
31596     
31597      
31598
31599     /**
31600      * Executes a Midas editor command on the editor document and performs necessary focus and
31601      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31602      * @param {String} cmd The Midas command
31603      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31604      */
31605     relayCmd : function(cmd, value)
31606     {
31607         
31608         switch (cmd) {
31609             case 'justifyleft':
31610             case 'justifyright':
31611             case 'justifycenter':
31612                 // if we are in a cell, then we will adjust the
31613                 var n = this.getParentElement();
31614                 var td = n.closest('td');
31615                 if (td) {
31616                     var bl = Roo.htmleditor.Block.factory(td);
31617                     bl.textAlign = cmd.replace('justify','');
31618                     bl.updateElement();
31619                     this.owner.fireEvent('editorevent', this);
31620                     return;
31621                 }
31622                 this.execCmd('styleWithCSS', true); // 
31623                 break;
31624             case 'bold':
31625             case 'italic':
31626                 // if there is no selection, then we insert, and set the curson inside it..
31627                 this.execCmd('styleWithCSS', false); 
31628                 break;
31629                 
31630         
31631             default:
31632                 break;
31633         }
31634         
31635         
31636         this.win.focus();
31637         this.execCmd(cmd, value);
31638         this.owner.fireEvent('editorevent', this);
31639         //this.updateToolbar();
31640         this.owner.deferFocus();
31641     },
31642
31643     /**
31644      * Executes a Midas editor command directly on the editor document.
31645      * For visual commands, you should use {@link #relayCmd} instead.
31646      * <b>This should only be called after the editor is initialized.</b>
31647      * @param {String} cmd The Midas command
31648      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31649      */
31650     execCmd : function(cmd, value){
31651         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31652         this.syncValue();
31653     },
31654  
31655  
31656    
31657     /**
31658      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31659      * to insert tRoo.
31660      * @param {String} text | dom node.. 
31661      */
31662     insertAtCursor : function(text)
31663     {
31664         
31665         if(!this.activated){
31666             return;
31667         }
31668          
31669         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31670             this.win.focus();
31671             
31672             
31673             // from jquery ui (MIT licenced)
31674             var range, node;
31675             var win = this.win;
31676             
31677             if (win.getSelection && win.getSelection().getRangeAt) {
31678                 
31679                 // delete the existing?
31680                 
31681                 this.createRange(this.getSelection()).deleteContents();
31682                 range = win.getSelection().getRangeAt(0);
31683                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31684                 range.insertNode(node);
31685                 range = range.cloneRange();
31686                 range.collapse(false);
31687                  
31688                 win.getSelection().removeAllRanges();
31689                 win.getSelection().addRange(range);
31690                 
31691                 
31692                 
31693             } else if (win.document.selection && win.document.selection.createRange) {
31694                 // no firefox support
31695                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31696                 win.document.selection.createRange().pasteHTML(txt);
31697             
31698             } else {
31699                 // no firefox support
31700                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31701                 this.execCmd('InsertHTML', txt);
31702             } 
31703             this.syncValue();
31704             
31705             this.deferFocus();
31706         }
31707     },
31708  // private
31709     mozKeyPress : function(e){
31710         if(e.ctrlKey){
31711             var c = e.getCharCode(), cmd;
31712           
31713             if(c > 0){
31714                 c = String.fromCharCode(c).toLowerCase();
31715                 switch(c){
31716                     case 'b':
31717                         cmd = 'bold';
31718                         break;
31719                     case 'i':
31720                         cmd = 'italic';
31721                         break;
31722                     
31723                     case 'u':
31724                         cmd = 'underline';
31725                         break;
31726                     
31727                     //case 'v':
31728                       //  this.cleanUpPaste.defer(100, this);
31729                       //  return;
31730                         
31731                 }
31732                 if(cmd){
31733                     
31734                     this.relayCmd(cmd);
31735                     //this.win.focus();
31736                     //this.execCmd(cmd);
31737                     //this.deferFocus();
31738                     e.preventDefault();
31739                 }
31740                 
31741             }
31742         }
31743     },
31744
31745     // private
31746     fixKeys : function(){ // load time branching for fastest keydown performance
31747         
31748         
31749         if(Roo.isIE){
31750             return function(e){
31751                 var k = e.getKey(), r;
31752                 if(k == e.TAB){
31753                     e.stopEvent();
31754                     r = this.doc.selection.createRange();
31755                     if(r){
31756                         r.collapse(true);
31757                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31758                         this.deferFocus();
31759                     }
31760                     return;
31761                 }
31762                 /// this is handled by Roo.htmleditor.KeyEnter
31763                  /*
31764                 if(k == e.ENTER){
31765                     r = this.doc.selection.createRange();
31766                     if(r){
31767                         var target = r.parentElement();
31768                         if(!target || target.tagName.toLowerCase() != 'li'){
31769                             e.stopEvent();
31770                             r.pasteHTML('<br/>');
31771                             r.collapse(false);
31772                             r.select();
31773                         }
31774                     }
31775                 }
31776                 */
31777                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31778                 //    this.cleanUpPaste.defer(100, this);
31779                 //    return;
31780                 //}
31781                 
31782                 
31783             };
31784         }else if(Roo.isOpera){
31785             return function(e){
31786                 var k = e.getKey();
31787                 if(k == e.TAB){
31788                     e.stopEvent();
31789                     this.win.focus();
31790                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31791                     this.deferFocus();
31792                 }
31793                
31794                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31795                 //    this.cleanUpPaste.defer(100, this);
31796                  //   return;
31797                 //}
31798                 
31799             };
31800         }else if(Roo.isSafari){
31801             return function(e){
31802                 var k = e.getKey();
31803                 
31804                 if(k == e.TAB){
31805                     e.stopEvent();
31806                     this.execCmd('InsertText','\t');
31807                     this.deferFocus();
31808                     return;
31809                 }
31810                  this.mozKeyPress(e);
31811                 
31812                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31813                  //   this.cleanUpPaste.defer(100, this);
31814                  //   return;
31815                // }
31816                 
31817              };
31818         }
31819     }(),
31820     
31821     getAllAncestors: function()
31822     {
31823         var p = this.getSelectedNode();
31824         var a = [];
31825         if (!p) {
31826             a.push(p); // push blank onto stack..
31827             p = this.getParentElement();
31828         }
31829         
31830         
31831         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31832             a.push(p);
31833             p = p.parentNode;
31834         }
31835         a.push(this.doc.body);
31836         return a;
31837     },
31838     lastSel : false,
31839     lastSelNode : false,
31840     
31841     
31842     getSelection : function() 
31843     {
31844         this.assignDocWin();
31845         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31846     },
31847     /**
31848      * Select a dom node
31849      * @param {DomElement} node the node to select
31850      */
31851     selectNode : function(node, collapse)
31852     {
31853         var nodeRange = node.ownerDocument.createRange();
31854         try {
31855             nodeRange.selectNode(node);
31856         } catch (e) {
31857             nodeRange.selectNodeContents(node);
31858         }
31859         if (collapse === true) {
31860             nodeRange.collapse(true);
31861         }
31862         //
31863         var s = this.win.getSelection();
31864         s.removeAllRanges();
31865         s.addRange(nodeRange);
31866     },
31867     
31868     getSelectedNode: function() 
31869     {
31870         // this may only work on Gecko!!!
31871         
31872         // should we cache this!!!!
31873         
31874          
31875          
31876         var range = this.createRange(this.getSelection()).cloneRange();
31877         
31878         if (Roo.isIE) {
31879             var parent = range.parentElement();
31880             while (true) {
31881                 var testRange = range.duplicate();
31882                 testRange.moveToElementText(parent);
31883                 if (testRange.inRange(range)) {
31884                     break;
31885                 }
31886                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31887                     break;
31888                 }
31889                 parent = parent.parentElement;
31890             }
31891             return parent;
31892         }
31893         
31894         // is ancestor a text element.
31895         var ac =  range.commonAncestorContainer;
31896         if (ac.nodeType == 3) {
31897             ac = ac.parentNode;
31898         }
31899         
31900         var ar = ac.childNodes;
31901          
31902         var nodes = [];
31903         var other_nodes = [];
31904         var has_other_nodes = false;
31905         for (var i=0;i<ar.length;i++) {
31906             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31907                 continue;
31908             }
31909             // fullly contained node.
31910             
31911             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31912                 nodes.push(ar[i]);
31913                 continue;
31914             }
31915             
31916             // probably selected..
31917             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31918                 other_nodes.push(ar[i]);
31919                 continue;
31920             }
31921             // outer..
31922             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31923                 continue;
31924             }
31925             
31926             
31927             has_other_nodes = true;
31928         }
31929         if (!nodes.length && other_nodes.length) {
31930             nodes= other_nodes;
31931         }
31932         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31933             return false;
31934         }
31935         
31936         return nodes[0];
31937     },
31938     
31939     
31940     createRange: function(sel)
31941     {
31942         // this has strange effects when using with 
31943         // top toolbar - not sure if it's a great idea.
31944         //this.editor.contentWindow.focus();
31945         if (typeof sel != "undefined") {
31946             try {
31947                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31948             } catch(e) {
31949                 return this.doc.createRange();
31950             }
31951         } else {
31952             return this.doc.createRange();
31953         }
31954     },
31955     getParentElement: function()
31956     {
31957         
31958         this.assignDocWin();
31959         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31960         
31961         var range = this.createRange(sel);
31962          
31963         try {
31964             var p = range.commonAncestorContainer;
31965             while (p.nodeType == 3) { // text node
31966                 p = p.parentNode;
31967             }
31968             return p;
31969         } catch (e) {
31970             return null;
31971         }
31972     
31973     },
31974     /***
31975      *
31976      * Range intersection.. the hard stuff...
31977      *  '-1' = before
31978      *  '0' = hits..
31979      *  '1' = after.
31980      *         [ -- selected range --- ]
31981      *   [fail]                        [fail]
31982      *
31983      *    basically..
31984      *      if end is before start or  hits it. fail.
31985      *      if start is after end or hits it fail.
31986      *
31987      *   if either hits (but other is outside. - then it's not 
31988      *   
31989      *    
31990      **/
31991     
31992     
31993     // @see http://www.thismuchiknow.co.uk/?p=64.
31994     rangeIntersectsNode : function(range, node)
31995     {
31996         var nodeRange = node.ownerDocument.createRange();
31997         try {
31998             nodeRange.selectNode(node);
31999         } catch (e) {
32000             nodeRange.selectNodeContents(node);
32001         }
32002     
32003         var rangeStartRange = range.cloneRange();
32004         rangeStartRange.collapse(true);
32005     
32006         var rangeEndRange = range.cloneRange();
32007         rangeEndRange.collapse(false);
32008     
32009         var nodeStartRange = nodeRange.cloneRange();
32010         nodeStartRange.collapse(true);
32011     
32012         var nodeEndRange = nodeRange.cloneRange();
32013         nodeEndRange.collapse(false);
32014     
32015         return rangeStartRange.compareBoundaryPoints(
32016                  Range.START_TO_START, nodeEndRange) == -1 &&
32017                rangeEndRange.compareBoundaryPoints(
32018                  Range.START_TO_START, nodeStartRange) == 1;
32019         
32020          
32021     },
32022     rangeCompareNode : function(range, node)
32023     {
32024         var nodeRange = node.ownerDocument.createRange();
32025         try {
32026             nodeRange.selectNode(node);
32027         } catch (e) {
32028             nodeRange.selectNodeContents(node);
32029         }
32030         
32031         
32032         range.collapse(true);
32033     
32034         nodeRange.collapse(true);
32035      
32036         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32037         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
32038          
32039         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32040         
32041         var nodeIsBefore   =  ss == 1;
32042         var nodeIsAfter    = ee == -1;
32043         
32044         if (nodeIsBefore && nodeIsAfter) {
32045             return 0; // outer
32046         }
32047         if (!nodeIsBefore && nodeIsAfter) {
32048             return 1; //right trailed.
32049         }
32050         
32051         if (nodeIsBefore && !nodeIsAfter) {
32052             return 2;  // left trailed.
32053         }
32054         // fully contined.
32055         return 3;
32056     },
32057  
32058     cleanWordChars : function(input) {// change the chars to hex code
32059         
32060        var swapCodes  = [ 
32061             [    8211, "&#8211;" ], 
32062             [    8212, "&#8212;" ], 
32063             [    8216,  "'" ],  
32064             [    8217, "'" ],  
32065             [    8220, '"' ],  
32066             [    8221, '"' ],  
32067             [    8226, "*" ],  
32068             [    8230, "..." ]
32069         ]; 
32070         var output = input;
32071         Roo.each(swapCodes, function(sw) { 
32072             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32073             
32074             output = output.replace(swapper, sw[1]);
32075         });
32076         
32077         return output;
32078     },
32079     
32080      
32081     
32082         
32083     
32084     cleanUpChild : function (node)
32085     {
32086         
32087         new Roo.htmleditor.FilterComment({node : node});
32088         new Roo.htmleditor.FilterAttributes({
32089                 node : node,
32090                 attrib_black : this.ablack,
32091                 attrib_clean : this.aclean,
32092                 style_white : this.cwhite,
32093                 style_black : this.cblack
32094         });
32095         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32096         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32097          
32098         
32099     },
32100     
32101     /**
32102      * Clean up MS wordisms...
32103      * @deprecated - use filter directly
32104      */
32105     cleanWord : function(node)
32106     {
32107         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32108         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32109         
32110     },
32111    
32112     
32113     /**
32114
32115      * @deprecated - use filters
32116      */
32117     cleanTableWidths : function(node)
32118     {
32119         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32120         
32121  
32122     },
32123     
32124      
32125         
32126     applyBlacklists : function()
32127     {
32128         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
32129         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
32130         
32131         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
32132         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
32133         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
32134         
32135         this.white = [];
32136         this.black = [];
32137         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32138             if (b.indexOf(tag) > -1) {
32139                 return;
32140             }
32141             this.white.push(tag);
32142             
32143         }, this);
32144         
32145         Roo.each(w, function(tag) {
32146             if (b.indexOf(tag) > -1) {
32147                 return;
32148             }
32149             if (this.white.indexOf(tag) > -1) {
32150                 return;
32151             }
32152             this.white.push(tag);
32153             
32154         }, this);
32155         
32156         
32157         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32158             if (w.indexOf(tag) > -1) {
32159                 return;
32160             }
32161             this.black.push(tag);
32162             
32163         }, this);
32164         
32165         Roo.each(b, function(tag) {
32166             if (w.indexOf(tag) > -1) {
32167                 return;
32168             }
32169             if (this.black.indexOf(tag) > -1) {
32170                 return;
32171             }
32172             this.black.push(tag);
32173             
32174         }, this);
32175         
32176         
32177         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
32178         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
32179         
32180         this.cwhite = [];
32181         this.cblack = [];
32182         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32183             if (b.indexOf(tag) > -1) {
32184                 return;
32185             }
32186             this.cwhite.push(tag);
32187             
32188         }, this);
32189         
32190         Roo.each(w, function(tag) {
32191             if (b.indexOf(tag) > -1) {
32192                 return;
32193             }
32194             if (this.cwhite.indexOf(tag) > -1) {
32195                 return;
32196             }
32197             this.cwhite.push(tag);
32198             
32199         }, this);
32200         
32201         
32202         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32203             if (w.indexOf(tag) > -1) {
32204                 return;
32205             }
32206             this.cblack.push(tag);
32207             
32208         }, this);
32209         
32210         Roo.each(b, function(tag) {
32211             if (w.indexOf(tag) > -1) {
32212                 return;
32213             }
32214             if (this.cblack.indexOf(tag) > -1) {
32215                 return;
32216             }
32217             this.cblack.push(tag);
32218             
32219         }, this);
32220     },
32221     
32222     setStylesheets : function(stylesheets)
32223     {
32224         if(typeof(stylesheets) == 'string'){
32225             Roo.get(this.iframe.contentDocument.head).createChild({
32226                 tag : 'link',
32227                 rel : 'stylesheet',
32228                 type : 'text/css',
32229                 href : stylesheets
32230             });
32231             
32232             return;
32233         }
32234         var _this = this;
32235      
32236         Roo.each(stylesheets, function(s) {
32237             if(!s.length){
32238                 return;
32239             }
32240             
32241             Roo.get(_this.iframe.contentDocument.head).createChild({
32242                 tag : 'link',
32243                 rel : 'stylesheet',
32244                 type : 'text/css',
32245                 href : s
32246             });
32247         });
32248
32249         
32250     },
32251     
32252     
32253     updateLanguage : function()
32254     {
32255         if (!this.iframe || !this.iframe.contentDocument) {
32256             return;
32257         }
32258         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32259     },
32260     
32261     
32262     removeStylesheets : function()
32263     {
32264         var _this = this;
32265         
32266         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32267             s.remove();
32268         });
32269     },
32270     
32271     setStyle : function(style)
32272     {
32273         Roo.get(this.iframe.contentDocument.head).createChild({
32274             tag : 'style',
32275             type : 'text/css',
32276             html : style
32277         });
32278
32279         return;
32280     }
32281     
32282     // hide stuff that is not compatible
32283     /**
32284      * @event blur
32285      * @hide
32286      */
32287     /**
32288      * @event change
32289      * @hide
32290      */
32291     /**
32292      * @event focus
32293      * @hide
32294      */
32295     /**
32296      * @event specialkey
32297      * @hide
32298      */
32299     /**
32300      * @cfg {String} fieldClass @hide
32301      */
32302     /**
32303      * @cfg {String} focusClass @hide
32304      */
32305     /**
32306      * @cfg {String} autoCreate @hide
32307      */
32308     /**
32309      * @cfg {String} inputType @hide
32310      */
32311     /**
32312      * @cfg {String} invalidClass @hide
32313      */
32314     /**
32315      * @cfg {String} invalidText @hide
32316      */
32317     /**
32318      * @cfg {String} msgFx @hide
32319      */
32320     /**
32321      * @cfg {String} validateOnBlur @hide
32322      */
32323 });
32324
32325 Roo.HtmlEditorCore.white = [
32326         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32327         
32328        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
32329        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
32330        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
32331        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
32332        'TABLE',   'UL',         'XMP', 
32333        
32334        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
32335       'THEAD',   'TR', 
32336      
32337       'DIR', 'MENU', 'OL', 'UL', 'DL',
32338        
32339       'EMBED',  'OBJECT'
32340 ];
32341
32342
32343 Roo.HtmlEditorCore.black = [
32344     //    'embed',  'object', // enable - backend responsiblity to clean thiese
32345         'APPLET', // 
32346         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
32347         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
32348         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
32349         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
32350         //'FONT' // CLEAN LATER..
32351         'COLGROUP', 'COL'   // messy tables.
32352         
32353         
32354 ];
32355 Roo.HtmlEditorCore.clean = [ // ?? needed???
32356      'SCRIPT', 'STYLE', 'TITLE', 'XML'
32357 ];
32358 Roo.HtmlEditorCore.tag_remove = [
32359     'FONT', 'TBODY'  
32360 ];
32361 // attributes..
32362
32363 Roo.HtmlEditorCore.ablack = [
32364     'on'
32365 ];
32366     
32367 Roo.HtmlEditorCore.aclean = [ 
32368     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
32369 ];
32370
32371 // protocols..
32372 Roo.HtmlEditorCore.pwhite= [
32373         'http',  'https',  'mailto'
32374 ];
32375
32376 // white listed style attributes.
32377 Roo.HtmlEditorCore.cwhite= [
32378       //  'text-align', /// default is to allow most things..
32379       
32380          
32381 //        'font-size'//??
32382 ];
32383
32384 // black listed style attributes.
32385 Roo.HtmlEditorCore.cblack= [
32386       //  'font-size' -- this can be set by the project 
32387 ];
32388
32389
32390
32391
32392     /*
32393  * - LGPL
32394  *
32395  * HtmlEditor
32396  * 
32397  */
32398
32399 /**
32400  * @class Roo.bootstrap.form.HtmlEditor
32401  * @extends Roo.bootstrap.form.TextArea
32402  * Bootstrap HtmlEditor class
32403
32404  * @constructor
32405  * Create a new HtmlEditor
32406  * @param {Object} config The config object
32407  */
32408
32409 Roo.bootstrap.form.HtmlEditor = function(config){
32410     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32411     if (!this.toolbars) {
32412         this.toolbars = [];
32413     }
32414     
32415     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32416     this.addEvents({
32417             /**
32418              * @event initialize
32419              * Fires when the editor is fully initialized (including the iframe)
32420              * @param {HtmlEditor} this
32421              */
32422             initialize: true,
32423             /**
32424              * @event activate
32425              * Fires when the editor is first receives the focus. Any insertion must wait
32426              * until after this event.
32427              * @param {HtmlEditor} this
32428              */
32429             activate: true,
32430              /**
32431              * @event beforesync
32432              * Fires before the textarea is updated with content from the editor iframe. Return false
32433              * to cancel the sync.
32434              * @param {HtmlEditor} this
32435              * @param {String} html
32436              */
32437             beforesync: true,
32438              /**
32439              * @event beforepush
32440              * Fires before the iframe editor is updated with content from the textarea. Return false
32441              * to cancel the push.
32442              * @param {HtmlEditor} this
32443              * @param {String} html
32444              */
32445             beforepush: true,
32446              /**
32447              * @event sync
32448              * Fires when the textarea is updated with content from the editor iframe.
32449              * @param {HtmlEditor} this
32450              * @param {String} html
32451              */
32452             sync: true,
32453              /**
32454              * @event push
32455              * Fires when the iframe editor is updated with content from the textarea.
32456              * @param {HtmlEditor} this
32457              * @param {String} html
32458              */
32459             push: true,
32460              /**
32461              * @event editmodechange
32462              * Fires when the editor switches edit modes
32463              * @param {HtmlEditor} this
32464              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32465              */
32466             editmodechange: true,
32467             /**
32468              * @event editorevent
32469              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32470              * @param {HtmlEditor} this
32471              */
32472             editorevent: true,
32473             /**
32474              * @event firstfocus
32475              * Fires when on first focus - needed by toolbars..
32476              * @param {HtmlEditor} this
32477              */
32478             firstfocus: true,
32479             /**
32480              * @event autosave
32481              * Auto save the htmlEditor value as a file into Events
32482              * @param {HtmlEditor} this
32483              */
32484             autosave: true,
32485             /**
32486              * @event savedpreview
32487              * preview the saved version of htmlEditor
32488              * @param {HtmlEditor} this
32489              */
32490             savedpreview: true
32491         });
32492 };
32493
32494
32495 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
32496     
32497     
32498       /**
32499      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
32500      */
32501     toolbars : false,
32502     
32503      /**
32504     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32505     */
32506     btns : [],
32507    
32508      /**
32509      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
32510      *                        Roo.resizable.
32511      */
32512     resizable : false,
32513      /**
32514      * @cfg {Number} height (in pixels)
32515      */   
32516     height: 300,
32517    /**
32518      * @cfg {Number} width (in pixels)
32519      */   
32520     width: false,
32521     
32522     /**
32523      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32524      * 
32525      */
32526     stylesheets: false,
32527     
32528     // id of frame..
32529     frameId: false,
32530     
32531     // private properties
32532     validationEvent : false,
32533     deferHeight: true,
32534     initialized : false,
32535     activated : false,
32536     
32537     onFocus : Roo.emptyFn,
32538     iframePad:3,
32539     hideMode:'offsets',
32540     
32541     tbContainer : false,
32542     
32543     bodyCls : '',
32544     
32545     toolbarContainer :function() {
32546         return this.wrap.select('.x-html-editor-tb',true).first();
32547     },
32548
32549     /**
32550      * Protected method that will not generally be called directly. It
32551      * is called when the editor creates its toolbar. Override this method if you need to
32552      * add custom toolbar buttons.
32553      * @param {HtmlEditor} editor
32554      */
32555     createToolbar : function(){
32556         Roo.log('renewing');
32557         Roo.log("create toolbars");
32558         
32559         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32560         this.toolbars[0].render(this.toolbarContainer());
32561         
32562         return;
32563         
32564 //        if (!editor.toolbars || !editor.toolbars.length) {
32565 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32566 //        }
32567 //        
32568 //        for (var i =0 ; i < editor.toolbars.length;i++) {
32569 //            editor.toolbars[i] = Roo.factory(
32570 //                    typeof(editor.toolbars[i]) == 'string' ?
32571 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
32572 //                Roo.bootstrap.form.HtmlEditor);
32573 //            editor.toolbars[i].init(editor);
32574 //        }
32575     },
32576
32577      
32578     // private
32579     onRender : function(ct, position)
32580     {
32581        // Roo.log("Call onRender: " + this.xtype);
32582         var _t = this;
32583         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32584       
32585         this.wrap = this.inputEl().wrap({
32586             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32587         });
32588         
32589         this.editorcore.onRender(ct, position);
32590          
32591         if (this.resizable) {
32592             this.resizeEl = new Roo.Resizable(this.wrap, {
32593                 pinned : true,
32594                 wrap: true,
32595                 dynamic : true,
32596                 minHeight : this.height,
32597                 height: this.height,
32598                 handles : this.resizable,
32599                 width: this.width,
32600                 listeners : {
32601                     resize : function(r, w, h) {
32602                         _t.onResize(w,h); // -something
32603                     }
32604                 }
32605             });
32606             
32607         }
32608         this.createToolbar(this);
32609        
32610         
32611         if(!this.width && this.resizable){
32612             this.setSize(this.wrap.getSize());
32613         }
32614         if (this.resizeEl) {
32615             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
32616             // should trigger onReize..
32617         }
32618         
32619     },
32620
32621     // private
32622     onResize : function(w, h)
32623     {
32624         Roo.log('resize: ' +w + ',' + h );
32625         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32626         var ew = false;
32627         var eh = false;
32628         
32629         if(this.inputEl() ){
32630             if(typeof w == 'number'){
32631                 var aw = w - this.wrap.getFrameWidth('lr');
32632                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32633                 ew = aw;
32634             }
32635             if(typeof h == 'number'){
32636                  var tbh = -11;  // fixme it needs to tool bar size!
32637                 for (var i =0; i < this.toolbars.length;i++) {
32638                     // fixme - ask toolbars for heights?
32639                     tbh += this.toolbars[i].el.getHeight();
32640                     //if (this.toolbars[i].footer) {
32641                     //    tbh += this.toolbars[i].footer.el.getHeight();
32642                     //}
32643                 }
32644               
32645                 
32646                 
32647                 
32648                 
32649                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32650                 ah -= 5; // knock a few pixes off for look..
32651                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32652                 var eh = ah;
32653             }
32654         }
32655         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32656         this.editorcore.onResize(ew,eh);
32657         
32658     },
32659
32660     /**
32661      * Toggles the editor between standard and source edit mode.
32662      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32663      */
32664     toggleSourceEdit : function(sourceEditMode)
32665     {
32666         this.editorcore.toggleSourceEdit(sourceEditMode);
32667         
32668         if(this.editorcore.sourceEditMode){
32669             Roo.log('editor - showing textarea');
32670             
32671 //            Roo.log('in');
32672 //            Roo.log(this.syncValue());
32673             this.syncValue();
32674             this.inputEl().removeClass(['hide', 'x-hidden']);
32675             this.inputEl().dom.removeAttribute('tabIndex');
32676             this.inputEl().focus();
32677         }else{
32678             Roo.log('editor - hiding textarea');
32679 //            Roo.log('out')
32680 //            Roo.log(this.pushValue()); 
32681             this.pushValue();
32682             
32683             this.inputEl().addClass(['hide', 'x-hidden']);
32684             this.inputEl().dom.setAttribute('tabIndex', -1);
32685             //this.deferFocus();
32686         }
32687          
32688         if(this.resizable){
32689             this.setSize(this.wrap.getSize());
32690         }
32691         
32692         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32693     },
32694  
32695     // private (for BoxComponent)
32696     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32697
32698     // private (for BoxComponent)
32699     getResizeEl : function(){
32700         return this.wrap;
32701     },
32702
32703     // private (for BoxComponent)
32704     getPositionEl : function(){
32705         return this.wrap;
32706     },
32707
32708     // private
32709     initEvents : function(){
32710         this.originalValue = this.getValue();
32711     },
32712
32713 //    /**
32714 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32715 //     * @method
32716 //     */
32717 //    markInvalid : Roo.emptyFn,
32718 //    /**
32719 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32720 //     * @method
32721 //     */
32722 //    clearInvalid : Roo.emptyFn,
32723
32724     setValue : function(v){
32725         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32726         this.editorcore.pushValue();
32727     },
32728
32729      
32730     // private
32731     deferFocus : function(){
32732         this.focus.defer(10, this);
32733     },
32734
32735     // doc'ed in Field
32736     focus : function(){
32737         this.editorcore.focus();
32738         
32739     },
32740       
32741
32742     // private
32743     onDestroy : function(){
32744         
32745         
32746         
32747         if(this.rendered){
32748             
32749             for (var i =0; i < this.toolbars.length;i++) {
32750                 // fixme - ask toolbars for heights?
32751                 this.toolbars[i].onDestroy();
32752             }
32753             
32754             this.wrap.dom.innerHTML = '';
32755             this.wrap.remove();
32756         }
32757     },
32758
32759     // private
32760     onFirstFocus : function(){
32761         //Roo.log("onFirstFocus");
32762         this.editorcore.onFirstFocus();
32763          for (var i =0; i < this.toolbars.length;i++) {
32764             this.toolbars[i].onFirstFocus();
32765         }
32766         
32767     },
32768     
32769     // private
32770     syncValue : function()
32771     {   
32772         this.editorcore.syncValue();
32773     },
32774     
32775     pushValue : function()
32776     {   
32777         this.editorcore.pushValue();
32778     }
32779      
32780     
32781     // hide stuff that is not compatible
32782     /**
32783      * @event blur
32784      * @hide
32785      */
32786     /**
32787      * @event change
32788      * @hide
32789      */
32790     /**
32791      * @event focus
32792      * @hide
32793      */
32794     /**
32795      * @event specialkey
32796      * @hide
32797      */
32798     /**
32799      * @cfg {String} fieldClass @hide
32800      */
32801     /**
32802      * @cfg {String} focusClass @hide
32803      */
32804     /**
32805      * @cfg {String} autoCreate @hide
32806      */
32807     /**
32808      * @cfg {String} inputType @hide
32809      */
32810      
32811     /**
32812      * @cfg {String} invalidText @hide
32813      */
32814     /**
32815      * @cfg {String} msgFx @hide
32816      */
32817     /**
32818      * @cfg {String} validateOnBlur @hide
32819      */
32820 });
32821  
32822     
32823    
32824    
32825    
32826       
32827 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32828 /**
32829  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32830  * @parent Roo.bootstrap.form.HtmlEditor
32831  * @extends Roo.bootstrap.nav.Simplebar
32832  * Basic Toolbar
32833  * 
32834  * @example
32835  * Usage:
32836  *
32837  new Roo.bootstrap.form.HtmlEditor({
32838     ....
32839     toolbars : [
32840         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32841             disable : { fonts: 1 , format: 1, ..., ... , ...],
32842             btns : [ .... ]
32843         })
32844     }
32845      
32846  * 
32847  * @cfg {Object} disable List of elements to disable..
32848  * @cfg {Array} btns List of additional buttons.
32849  * 
32850  * 
32851  * NEEDS Extra CSS? 
32852  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32853  */
32854  
32855 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32856 {
32857     
32858     Roo.apply(this, config);
32859     
32860     // default disabled, based on 'good practice'..
32861     this.disable = this.disable || {};
32862     Roo.applyIf(this.disable, {
32863         fontSize : true,
32864         colors : true,
32865         specialElements : true
32866     });
32867     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32868     
32869     this.editor = config.editor;
32870     this.editorcore = config.editor.editorcore;
32871     
32872     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32873     
32874     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32875     // dont call parent... till later.
32876 }
32877 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
32878      
32879     bar : true,
32880     
32881     editor : false,
32882     editorcore : false,
32883     
32884     
32885     formats : [
32886         "p" ,  
32887         "h1","h2","h3","h4","h5","h6", 
32888         "pre", "code", 
32889         "abbr", "acronym", "address", "cite", "samp", "var",
32890         'div','span'
32891     ],
32892     
32893     onRender : function(ct, position)
32894     {
32895        // Roo.log("Call onRender: " + this.xtype);
32896         
32897        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32898        Roo.log(this.el);
32899        this.el.dom.style.marginBottom = '0';
32900        var _this = this;
32901        var editorcore = this.editorcore;
32902        var editor= this.editor;
32903        
32904        var children = [];
32905        var btn = function(id,cmd , toggle, handler, html){
32906        
32907             var  event = toggle ? 'toggle' : 'click';
32908        
32909             var a = {
32910                 size : 'sm',
32911                 xtype: 'Button',
32912                 xns: Roo.bootstrap,
32913                 //glyphicon : id,
32914                 fa: id,
32915                 cmd : id || cmd,
32916                 enableToggle:toggle !== false,
32917                 html : html || '',
32918                 pressed : toggle ? false : null,
32919                 listeners : {}
32920             };
32921             a.listeners[toggle ? 'toggle' : 'click'] = function() {
32922                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
32923             };
32924             children.push(a);
32925             return a;
32926        }
32927        
32928     //    var cb_box = function...
32929         
32930         var style = {
32931                 xtype: 'Button',
32932                 size : 'sm',
32933                 xns: Roo.bootstrap,
32934                 fa : 'font',
32935                 //html : 'submit'
32936                 menu : {
32937                     xtype: 'Menu',
32938                     xns: Roo.bootstrap,
32939                     items:  []
32940                 }
32941         };
32942         Roo.each(this.formats, function(f) {
32943             style.menu.items.push({
32944                 xtype :'MenuItem',
32945                 xns: Roo.bootstrap,
32946                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32947                 tagname : f,
32948                 listeners : {
32949                     click : function()
32950                     {
32951                         editorcore.insertTag(this.tagname);
32952                         editor.focus();
32953                     }
32954                 }
32955                 
32956             });
32957         });
32958         children.push(style);   
32959         
32960         btn('bold',false,true);
32961         btn('italic',false,true);
32962         btn('align-left', 'justifyleft',true);
32963         btn('align-center', 'justifycenter',true);
32964         btn('align-right' , 'justifyright',true);
32965         btn('link', false, false, function(btn) {
32966             //Roo.log("create link?");
32967             var url = prompt(this.createLinkText, this.defaultLinkValue);
32968             if(url && url != 'http:/'+'/'){
32969                 this.editorcore.relayCmd('createlink', url);
32970             }
32971         }),
32972         btn('list','insertunorderedlist',true);
32973         btn('pencil', false,true, function(btn){
32974                 Roo.log(this);
32975                 this.toggleSourceEdit(btn.pressed);
32976         });
32977         
32978         if (this.editor.btns.length > 0) {
32979             for (var i = 0; i<this.editor.btns.length; i++) {
32980                 children.push(this.editor.btns[i]);
32981             }
32982         }
32983         
32984         /*
32985         var cog = {
32986                 xtype: 'Button',
32987                 size : 'sm',
32988                 xns: Roo.bootstrap,
32989                 glyphicon : 'cog',
32990                 //html : 'submit'
32991                 menu : {
32992                     xtype: 'Menu',
32993                     xns: Roo.bootstrap,
32994                     items:  []
32995                 }
32996         };
32997         
32998         cog.menu.items.push({
32999             xtype :'MenuItem',
33000             xns: Roo.bootstrap,
33001             html : Clean styles,
33002             tagname : f,
33003             listeners : {
33004                 click : function()
33005                 {
33006                     editorcore.insertTag(this.tagname);
33007                     editor.focus();
33008                 }
33009             }
33010             
33011         });
33012        */
33013         
33014          
33015        this.xtype = 'NavSimplebar';
33016         
33017         for(var i=0;i< children.length;i++) {
33018             
33019             this.buttons.add(this.addxtypeChild(children[i]));
33020             
33021         }
33022         
33023         editor.on('editorevent', this.updateToolbar, this);
33024     },
33025     onBtnClick : function(id)
33026     {
33027        this.editorcore.relayCmd(id);
33028        this.editorcore.focus();
33029     },
33030     
33031     /**
33032      * Protected method that will not generally be called directly. It triggers
33033      * a toolbar update by reading the markup state of the current selection in the editor.
33034      */
33035     updateToolbar: function(){
33036
33037         if(!this.editorcore.activated){
33038             this.editor.onFirstFocus(); // is this neeed?
33039             return;
33040         }
33041
33042         var btns = this.buttons; 
33043         var doc = this.editorcore.doc;
33044         btns.get('bold').setActive(doc.queryCommandState('bold'));
33045         btns.get('italic').setActive(doc.queryCommandState('italic'));
33046         //btns.get('underline').setActive(doc.queryCommandState('underline'));
33047         
33048         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
33049         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
33050         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
33051         
33052         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
33053         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
33054          /*
33055         
33056         var ans = this.editorcore.getAllAncestors();
33057         if (this.formatCombo) {
33058             
33059             
33060             var store = this.formatCombo.store;
33061             this.formatCombo.setValue("");
33062             for (var i =0; i < ans.length;i++) {
33063                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
33064                     // select it..
33065                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
33066                     break;
33067                 }
33068             }
33069         }
33070         
33071         
33072         
33073         // hides menus... - so this cant be on a menu...
33074         Roo.bootstrap.MenuMgr.hideAll();
33075         */
33076         Roo.bootstrap.menu.Manager.hideAll();
33077         //this.editorsyncValue();
33078     },
33079     onFirstFocus: function() {
33080         this.buttons.each(function(item){
33081            item.enable();
33082         });
33083     },
33084     toggleSourceEdit : function(sourceEditMode){
33085         
33086           
33087         if(sourceEditMode){
33088             Roo.log("disabling buttons");
33089            this.buttons.each( function(item){
33090                 if(item.cmd != 'pencil'){
33091                     item.disable();
33092                 }
33093             });
33094           
33095         }else{
33096             Roo.log("enabling buttons");
33097             if(this.editorcore.initialized){
33098                 this.buttons.each( function(item){
33099                     item.enable();
33100                 });
33101             }
33102             
33103         }
33104         Roo.log("calling toggole on editor");
33105         // tell the editor that it's been pressed..
33106         this.editor.toggleSourceEdit(sourceEditMode);
33107        
33108     }
33109 });
33110
33111
33112
33113
33114  
33115 /*
33116  * - LGPL
33117  */
33118
33119 /**
33120  * @class Roo.bootstrap.form.Markdown
33121  * @extends Roo.bootstrap.form.TextArea
33122  * Bootstrap Showdown editable area
33123  * @cfg {string} content
33124  * 
33125  * @constructor
33126  * Create a new Showdown
33127  */
33128
33129 Roo.bootstrap.form.Markdown = function(config){
33130     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33131    
33132 };
33133
33134 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
33135     
33136     editing :false,
33137     
33138     initEvents : function()
33139     {
33140         
33141         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33142         this.markdownEl = this.el.createChild({
33143             cls : 'roo-markdown-area'
33144         });
33145         this.inputEl().addClass('d-none');
33146         if (this.getValue() == '') {
33147             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33148             
33149         } else {
33150             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33151         }
33152         this.markdownEl.on('click', this.toggleTextEdit, this);
33153         this.on('blur', this.toggleTextEdit, this);
33154         this.on('specialkey', this.resizeTextArea, this);
33155     },
33156     
33157     toggleTextEdit : function()
33158     {
33159         var sh = this.markdownEl.getHeight();
33160         this.inputEl().addClass('d-none');
33161         this.markdownEl.addClass('d-none');
33162         if (!this.editing) {
33163             // show editor?
33164             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33165             this.inputEl().removeClass('d-none');
33166             this.inputEl().focus();
33167             this.editing = true;
33168             return;
33169         }
33170         // show showdown...
33171         this.updateMarkdown();
33172         this.markdownEl.removeClass('d-none');
33173         this.editing = false;
33174         return;
33175     },
33176     updateMarkdown : function()
33177     {
33178         if (this.getValue() == '') {
33179             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33180             return;
33181         }
33182  
33183         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33184     },
33185     
33186     resizeTextArea: function () {
33187         
33188         var sh = 100;
33189         Roo.log([sh, this.getValue().split("\n").length * 30]);
33190         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33191     },
33192     setValue : function(val)
33193     {
33194         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33195         if (!this.editing) {
33196             this.updateMarkdown();
33197         }
33198         
33199     },
33200     focus : function()
33201     {
33202         if (!this.editing) {
33203             this.toggleTextEdit();
33204         }
33205         
33206     }
33207
33208
33209 });/*
33210  * Based on:
33211  * Ext JS Library 1.1.1
33212  * Copyright(c) 2006-2007, Ext JS, LLC.
33213  *
33214  * Originally Released Under LGPL - original licence link has changed is not relivant.
33215  *
33216  * Fork - LGPL
33217  * <script type="text/javascript">
33218  */
33219  
33220 /**
33221  * @class Roo.bootstrap.PagingToolbar
33222  * @extends Roo.bootstrap.nav.Simplebar
33223  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33224  * @constructor
33225  * Create a new PagingToolbar
33226  * @param {Object} config The config object
33227  * @param {Roo.data.Store} store
33228  */
33229 Roo.bootstrap.PagingToolbar = function(config)
33230 {
33231     // old args format still supported... - xtype is prefered..
33232         // created from xtype...
33233     
33234     this.ds = config.dataSource;
33235     
33236     if (config.store && !this.ds) {
33237         this.store= Roo.factory(config.store, Roo.data);
33238         this.ds = this.store;
33239         this.ds.xmodule = this.xmodule || false;
33240     }
33241     
33242     this.toolbarItems = [];
33243     if (config.items) {
33244         this.toolbarItems = config.items;
33245     }
33246     
33247     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33248     
33249     this.cursor = 0;
33250     
33251     if (this.ds) { 
33252         this.bind(this.ds);
33253     }
33254     
33255     if (Roo.bootstrap.version == 4) {
33256         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33257     } else {
33258         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33259     }
33260     
33261 };
33262
33263 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33264     /**
33265      * @cfg {Roo.bootstrap.Button} buttons[]
33266      * Buttons for the toolbar
33267      */
33268      /**
33269      * @cfg {Roo.data.Store} store
33270      * The underlying data store providing the paged data
33271      */
33272     /**
33273      * @cfg {String/HTMLElement/Element} container
33274      * container The id or element that will contain the toolbar
33275      */
33276     /**
33277      * @cfg {Boolean} displayInfo
33278      * True to display the displayMsg (defaults to false)
33279      */
33280     /**
33281      * @cfg {Number} pageSize
33282      * The number of records to display per page (defaults to 20)
33283      */
33284     pageSize: 20,
33285     /**
33286      * @cfg {String} displayMsg
33287      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33288      */
33289     displayMsg : 'Displaying {0} - {1} of {2}',
33290     /**
33291      * @cfg {String} emptyMsg
33292      * The message to display when no records are found (defaults to "No data to display")
33293      */
33294     emptyMsg : 'No data to display',
33295     /**
33296      * Customizable piece of the default paging text (defaults to "Page")
33297      * @type String
33298      */
33299     beforePageText : "Page",
33300     /**
33301      * Customizable piece of the default paging text (defaults to "of %0")
33302      * @type String
33303      */
33304     afterPageText : "of {0}",
33305     /**
33306      * Customizable piece of the default paging text (defaults to "First Page")
33307      * @type String
33308      */
33309     firstText : "First Page",
33310     /**
33311      * Customizable piece of the default paging text (defaults to "Previous Page")
33312      * @type String
33313      */
33314     prevText : "Previous Page",
33315     /**
33316      * Customizable piece of the default paging text (defaults to "Next Page")
33317      * @type String
33318      */
33319     nextText : "Next Page",
33320     /**
33321      * Customizable piece of the default paging text (defaults to "Last Page")
33322      * @type String
33323      */
33324     lastText : "Last Page",
33325     /**
33326      * Customizable piece of the default paging text (defaults to "Refresh")
33327      * @type String
33328      */
33329     refreshText : "Refresh",
33330
33331     buttons : false,
33332     // private
33333     onRender : function(ct, position) 
33334     {
33335         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33336         this.navgroup.parentId = this.id;
33337         this.navgroup.onRender(this.el, null);
33338         // add the buttons to the navgroup
33339         
33340         if(this.displayInfo){
33341             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33342             this.displayEl = this.el.select('.x-paging-info', true).first();
33343 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33344 //            this.displayEl = navel.el.select('span',true).first();
33345         }
33346         
33347         var _this = this;
33348         
33349         if(this.buttons){
33350             Roo.each(_this.buttons, function(e){ // this might need to use render????
33351                Roo.factory(e).render(_this.el);
33352             });
33353         }
33354             
33355         Roo.each(_this.toolbarItems, function(e) {
33356             _this.navgroup.addItem(e);
33357         });
33358         
33359         
33360         this.first = this.navgroup.addItem({
33361             tooltip: this.firstText,
33362             cls: "prev btn-outline-secondary",
33363             html : ' <i class="fa fa-step-backward"></i>',
33364             disabled: true,
33365             preventDefault: true,
33366             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33367         });
33368         
33369         this.prev =  this.navgroup.addItem({
33370             tooltip: this.prevText,
33371             cls: "prev btn-outline-secondary",
33372             html : ' <i class="fa fa-backward"></i>',
33373             disabled: true,
33374             preventDefault: true,
33375             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
33376         });
33377     //this.addSeparator();
33378         
33379         
33380         var field = this.navgroup.addItem( {
33381             tagtype : 'span',
33382             cls : 'x-paging-position  btn-outline-secondary',
33383              disabled: true,
33384             html : this.beforePageText  +
33385                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33386                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
33387          } ); //?? escaped?
33388         
33389         this.field = field.el.select('input', true).first();
33390         this.field.on("keydown", this.onPagingKeydown, this);
33391         this.field.on("focus", function(){this.dom.select();});
33392     
33393     
33394         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
33395         //this.field.setHeight(18);
33396         //this.addSeparator();
33397         this.next = this.navgroup.addItem({
33398             tooltip: this.nextText,
33399             cls: "next btn-outline-secondary",
33400             html : ' <i class="fa fa-forward"></i>',
33401             disabled: true,
33402             preventDefault: true,
33403             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
33404         });
33405         this.last = this.navgroup.addItem({
33406             tooltip: this.lastText,
33407             html : ' <i class="fa fa-step-forward"></i>',
33408             cls: "next btn-outline-secondary",
33409             disabled: true,
33410             preventDefault: true,
33411             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
33412         });
33413     //this.addSeparator();
33414         this.loading = this.navgroup.addItem({
33415             tooltip: this.refreshText,
33416             cls: "btn-outline-secondary",
33417             html : ' <i class="fa fa-refresh"></i>',
33418             preventDefault: true,
33419             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33420         });
33421         
33422     },
33423
33424     // private
33425     updateInfo : function(){
33426         if(this.displayEl){
33427             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33428             var msg = count == 0 ?
33429                 this.emptyMsg :
33430                 String.format(
33431                     this.displayMsg,
33432                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
33433                 );
33434             this.displayEl.update(msg);
33435         }
33436     },
33437
33438     // private
33439     onLoad : function(ds, r, o)
33440     {
33441         this.cursor = o.params && o.params.start ? o.params.start : 0;
33442         
33443         var d = this.getPageData(),
33444             ap = d.activePage,
33445             ps = d.pages;
33446         
33447         
33448         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33449         this.field.dom.value = ap;
33450         this.first.setDisabled(ap == 1);
33451         this.prev.setDisabled(ap == 1);
33452         this.next.setDisabled(ap == ps);
33453         this.last.setDisabled(ap == ps);
33454         this.loading.enable();
33455         this.updateInfo();
33456     },
33457
33458     // private
33459     getPageData : function(){
33460         var total = this.ds.getTotalCount();
33461         return {
33462             total : total,
33463             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33464             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33465         };
33466     },
33467
33468     // private
33469     onLoadError : function(proxy, o){
33470         this.loading.enable();
33471         if (this.ds.events.loadexception.listeners.length  < 2) {
33472             // nothing has been assigned to loadexception except this...
33473             // so 
33474             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33475
33476         }
33477     },
33478
33479     // private
33480     onPagingKeydown : function(e){
33481         var k = e.getKey();
33482         var d = this.getPageData();
33483         if(k == e.RETURN){
33484             var v = this.field.dom.value, pageNum;
33485             if(!v || isNaN(pageNum = parseInt(v, 10))){
33486                 this.field.dom.value = d.activePage;
33487                 return;
33488             }
33489             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33490             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33491             e.stopEvent();
33492         }
33493         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))
33494         {
33495           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33496           this.field.dom.value = pageNum;
33497           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33498           e.stopEvent();
33499         }
33500         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33501         {
33502           var v = this.field.dom.value, pageNum; 
33503           var increment = (e.shiftKey) ? 10 : 1;
33504           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33505                 increment *= -1;
33506           }
33507           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33508             this.field.dom.value = d.activePage;
33509             return;
33510           }
33511           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33512           {
33513             this.field.dom.value = parseInt(v, 10) + increment;
33514             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33515             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33516           }
33517           e.stopEvent();
33518         }
33519     },
33520
33521     // private
33522     beforeLoad : function(){
33523         if(this.loading){
33524             this.loading.disable();
33525         }
33526     },
33527
33528     // private
33529     onClick : function(which){
33530         
33531         var ds = this.ds;
33532         if (!ds) {
33533             return;
33534         }
33535         
33536         switch(which){
33537             case "first":
33538                 ds.load({params:{start: 0, limit: this.pageSize}});
33539             break;
33540             case "prev":
33541                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33542             break;
33543             case "next":
33544                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33545             break;
33546             case "last":
33547                 var total = ds.getTotalCount();
33548                 var extra = total % this.pageSize;
33549                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33550                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33551             break;
33552             case "refresh":
33553                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33554             break;
33555         }
33556     },
33557
33558     /**
33559      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33560      * @param {Roo.data.Store} store The data store to unbind
33561      */
33562     unbind : function(ds){
33563         ds.un("beforeload", this.beforeLoad, this);
33564         ds.un("load", this.onLoad, this);
33565         ds.un("loadexception", this.onLoadError, this);
33566         ds.un("remove", this.updateInfo, this);
33567         ds.un("add", this.updateInfo, this);
33568         this.ds = undefined;
33569     },
33570
33571     /**
33572      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33573      * @param {Roo.data.Store} store The data store to bind
33574      */
33575     bind : function(ds){
33576         ds.on("beforeload", this.beforeLoad, this);
33577         ds.on("load", this.onLoad, this);
33578         ds.on("loadexception", this.onLoadError, this);
33579         ds.on("remove", this.updateInfo, this);
33580         ds.on("add", this.updateInfo, this);
33581         this.ds = ds;
33582     }
33583 });/*
33584  * - LGPL
33585  *
33586  * element
33587  * 
33588  */
33589
33590 /**
33591  * @class Roo.bootstrap.MessageBar
33592  * @extends Roo.bootstrap.Component
33593  * Bootstrap MessageBar class
33594  * @cfg {String} html contents of the MessageBar
33595  * @cfg {String} weight (info | success | warning | danger) default info
33596  * @cfg {String} beforeClass insert the bar before the given class
33597  * @cfg {Boolean} closable (true | false) default false
33598  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33599  * 
33600  * @constructor
33601  * Create a new Element
33602  * @param {Object} config The config object
33603  */
33604
33605 Roo.bootstrap.MessageBar = function(config){
33606     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33607 };
33608
33609 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33610     
33611     html: '',
33612     weight: 'info',
33613     closable: false,
33614     fixed: false,
33615     beforeClass: 'bootstrap-sticky-wrap',
33616     
33617     getAutoCreate : function(){
33618         
33619         var cfg = {
33620             tag: 'div',
33621             cls: 'alert alert-dismissable alert-' + this.weight,
33622             cn: [
33623                 {
33624                     tag: 'span',
33625                     cls: 'message',
33626                     html: this.html || ''
33627                 }
33628             ]
33629         };
33630         
33631         if(this.fixed){
33632             cfg.cls += ' alert-messages-fixed';
33633         }
33634         
33635         if(this.closable){
33636             cfg.cn.push({
33637                 tag: 'button',
33638                 cls: 'close',
33639                 html: 'x'
33640             });
33641         }
33642         
33643         return cfg;
33644     },
33645     
33646     onRender : function(ct, position)
33647     {
33648         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33649         
33650         if(!this.el){
33651             var cfg = Roo.apply({},  this.getAutoCreate());
33652             cfg.id = Roo.id();
33653             
33654             if (this.cls) {
33655                 cfg.cls += ' ' + this.cls;
33656             }
33657             if (this.style) {
33658                 cfg.style = this.style;
33659             }
33660             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33661             
33662             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33663         }
33664         
33665         this.el.select('>button.close').on('click', this.hide, this);
33666         
33667     },
33668     
33669     show : function()
33670     {
33671         if (!this.rendered) {
33672             this.render();
33673         }
33674         
33675         this.el.show();
33676         
33677         this.fireEvent('show', this);
33678         
33679     },
33680     
33681     hide : function()
33682     {
33683         if (!this.rendered) {
33684             this.render();
33685         }
33686         
33687         this.el.hide();
33688         
33689         this.fireEvent('hide', this);
33690     },
33691     
33692     update : function()
33693     {
33694 //        var e = this.el.dom.firstChild;
33695 //        
33696 //        if(this.closable){
33697 //            e = e.nextSibling;
33698 //        }
33699 //        
33700 //        e.data = this.html || '';
33701
33702         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33703     }
33704    
33705 });
33706
33707  
33708
33709      /*
33710  * - LGPL
33711  *
33712  * Graph
33713  * 
33714  */
33715
33716
33717 /**
33718  * @class Roo.bootstrap.Graph
33719  * @extends Roo.bootstrap.Component
33720  * Bootstrap Graph class
33721 > Prameters
33722  -sm {number} sm 4
33723  -md {number} md 5
33724  @cfg {String} graphtype  bar | vbar | pie
33725  @cfg {number} g_x coodinator | centre x (pie)
33726  @cfg {number} g_y coodinator | centre y (pie)
33727  @cfg {number} g_r radius (pie)
33728  @cfg {number} g_height height of the chart (respected by all elements in the set)
33729  @cfg {number} g_width width of the chart (respected by all elements in the set)
33730  @cfg {Object} title The title of the chart
33731     
33732  -{Array}  values
33733  -opts (object) options for the chart 
33734      o {
33735      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33736      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33737      o vgutter (number)
33738      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.
33739      o stacked (boolean) whether or not to tread values as in a stacked bar chart
33740      o to
33741      o stretch (boolean)
33742      o }
33743  -opts (object) options for the pie
33744      o{
33745      o cut
33746      o startAngle (number)
33747      o endAngle (number)
33748      } 
33749  *
33750  * @constructor
33751  * Create a new Input
33752  * @param {Object} config The config object
33753  */
33754
33755 Roo.bootstrap.Graph = function(config){
33756     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33757     
33758     this.addEvents({
33759         // img events
33760         /**
33761          * @event click
33762          * The img click event for the img.
33763          * @param {Roo.EventObject} e
33764          */
33765         "click" : true
33766     });
33767 };
33768
33769 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
33770     
33771     sm: 4,
33772     md: 5,
33773     graphtype: 'bar',
33774     g_height: 250,
33775     g_width: 400,
33776     g_x: 50,
33777     g_y: 50,
33778     g_r: 30,
33779     opts:{
33780         //g_colors: this.colors,
33781         g_type: 'soft',
33782         g_gutter: '20%'
33783
33784     },
33785     title : false,
33786
33787     getAutoCreate : function(){
33788         
33789         var cfg = {
33790             tag: 'div',
33791             html : null
33792         };
33793         
33794         
33795         return  cfg;
33796     },
33797
33798     onRender : function(ct,position){
33799         
33800         
33801         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33802         
33803         if (typeof(Raphael) == 'undefined') {
33804             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33805             return;
33806         }
33807         
33808         this.raphael = Raphael(this.el.dom);
33809         
33810                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33811                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33812                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33813                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33814                 /*
33815                 r.text(160, 10, "Single Series Chart").attr(txtattr);
33816                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33817                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33818                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33819                 
33820                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33821                 r.barchart(330, 10, 300, 220, data1);
33822                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33823                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33824                 */
33825                 
33826                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33827                 // r.barchart(30, 30, 560, 250,  xdata, {
33828                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33829                 //     axis : "0 0 1 1",
33830                 //     axisxlabels :  xdata
33831                 //     //yvalues : cols,
33832                    
33833                 // });
33834 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33835 //        
33836 //        this.load(null,xdata,{
33837 //                axis : "0 0 1 1",
33838 //                axisxlabels :  xdata
33839 //                });
33840
33841     },
33842
33843     load : function(graphtype,xdata,opts)
33844     {
33845         this.raphael.clear();
33846         if(!graphtype) {
33847             graphtype = this.graphtype;
33848         }
33849         if(!opts){
33850             opts = this.opts;
33851         }
33852         var r = this.raphael,
33853             fin = function () {
33854                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33855             },
33856             fout = function () {
33857                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33858             },
33859             pfin = function() {
33860                 this.sector.stop();
33861                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33862
33863                 if (this.label) {
33864                     this.label[0].stop();
33865                     this.label[0].attr({ r: 7.5 });
33866                     this.label[1].attr({ "font-weight": 800 });
33867                 }
33868             },
33869             pfout = function() {
33870                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33871
33872                 if (this.label) {
33873                     this.label[0].animate({ r: 5 }, 500, "bounce");
33874                     this.label[1].attr({ "font-weight": 400 });
33875                 }
33876             };
33877
33878         switch(graphtype){
33879             case 'bar':
33880                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33881                 break;
33882             case 'hbar':
33883                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33884                 break;
33885             case 'pie':
33886 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
33887 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33888 //            
33889                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33890                 
33891                 break;
33892
33893         }
33894         
33895         if(this.title){
33896             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33897         }
33898         
33899     },
33900     
33901     setTitle: function(o)
33902     {
33903         this.title = o;
33904     },
33905     
33906     initEvents: function() {
33907         
33908         if(!this.href){
33909             this.el.on('click', this.onClick, this);
33910         }
33911     },
33912     
33913     onClick : function(e)
33914     {
33915         Roo.log('img onclick');
33916         this.fireEvent('click', this, e);
33917     }
33918    
33919 });
33920
33921  
33922 Roo.bootstrap.dash = {};/*
33923  * - LGPL
33924  *
33925  * numberBox
33926  * 
33927  */
33928 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33929
33930 /**
33931  * @class Roo.bootstrap.dash.NumberBox
33932  * @extends Roo.bootstrap.Component
33933  * Bootstrap NumberBox class
33934  * @cfg {String} headline Box headline
33935  * @cfg {String} content Box content
33936  * @cfg {String} icon Box icon
33937  * @cfg {String} footer Footer text
33938  * @cfg {String} fhref Footer href
33939  * 
33940  * @constructor
33941  * Create a new NumberBox
33942  * @param {Object} config The config object
33943  */
33944
33945
33946 Roo.bootstrap.dash.NumberBox = function(config){
33947     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33948     
33949 };
33950
33951 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
33952     
33953     headline : '',
33954     content : '',
33955     icon : '',
33956     footer : '',
33957     fhref : '',
33958     ficon : '',
33959     
33960     getAutoCreate : function(){
33961         
33962         var cfg = {
33963             tag : 'div',
33964             cls : 'small-box ',
33965             cn : [
33966                 {
33967                     tag : 'div',
33968                     cls : 'inner',
33969                     cn :[
33970                         {
33971                             tag : 'h3',
33972                             cls : 'roo-headline',
33973                             html : this.headline
33974                         },
33975                         {
33976                             tag : 'p',
33977                             cls : 'roo-content',
33978                             html : this.content
33979                         }
33980                     ]
33981                 }
33982             ]
33983         };
33984         
33985         if(this.icon){
33986             cfg.cn.push({
33987                 tag : 'div',
33988                 cls : 'icon',
33989                 cn :[
33990                     {
33991                         tag : 'i',
33992                         cls : 'ion ' + this.icon
33993                     }
33994                 ]
33995             });
33996         }
33997         
33998         if(this.footer){
33999             var footer = {
34000                 tag : 'a',
34001                 cls : 'small-box-footer',
34002                 href : this.fhref || '#',
34003                 html : this.footer
34004             };
34005             
34006             cfg.cn.push(footer);
34007             
34008         }
34009         
34010         return  cfg;
34011     },
34012
34013     onRender : function(ct,position){
34014         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
34015
34016
34017        
34018                 
34019     },
34020
34021     setHeadline: function (value)
34022     {
34023         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34024     },
34025     
34026     setFooter: function (value, href)
34027     {
34028         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34029         
34030         if(href){
34031             this.el.select('a.small-box-footer',true).first().attr('href', href);
34032         }
34033         
34034     },
34035
34036     setContent: function (value)
34037     {
34038         this.el.select('.roo-content',true).first().dom.innerHTML = value;
34039     },
34040
34041     initEvents: function() 
34042     {   
34043         
34044     }
34045     
34046 });
34047
34048  
34049 /*
34050  * - LGPL
34051  *
34052  * TabBox
34053  * 
34054  */
34055 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34056
34057 /**
34058  * @class Roo.bootstrap.dash.TabBox
34059  * @extends Roo.bootstrap.Component
34060  * @children Roo.bootstrap.dash.TabPane
34061  * Bootstrap TabBox class
34062  * @cfg {String} title Title of the TabBox
34063  * @cfg {String} icon Icon of the TabBox
34064  * @cfg {Boolean} showtabs (true|false) show the tabs default true
34065  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34066  * 
34067  * @constructor
34068  * Create a new TabBox
34069  * @param {Object} config The config object
34070  */
34071
34072
34073 Roo.bootstrap.dash.TabBox = function(config){
34074     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34075     this.addEvents({
34076         // raw events
34077         /**
34078          * @event addpane
34079          * When a pane is added
34080          * @param {Roo.bootstrap.dash.TabPane} pane
34081          */
34082         "addpane" : true,
34083         /**
34084          * @event activatepane
34085          * When a pane is activated
34086          * @param {Roo.bootstrap.dash.TabPane} pane
34087          */
34088         "activatepane" : true
34089         
34090          
34091     });
34092     
34093     this.panes = [];
34094 };
34095
34096 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
34097
34098     title : '',
34099     icon : false,
34100     showtabs : true,
34101     tabScrollable : false,
34102     
34103     getChildContainer : function()
34104     {
34105         return this.el.select('.tab-content', true).first();
34106     },
34107     
34108     getAutoCreate : function(){
34109         
34110         var header = {
34111             tag: 'li',
34112             cls: 'pull-left header',
34113             html: this.title,
34114             cn : []
34115         };
34116         
34117         if(this.icon){
34118             header.cn.push({
34119                 tag: 'i',
34120                 cls: 'fa ' + this.icon
34121             });
34122         }
34123         
34124         var h = {
34125             tag: 'ul',
34126             cls: 'nav nav-tabs pull-right',
34127             cn: [
34128                 header
34129             ]
34130         };
34131         
34132         if(this.tabScrollable){
34133             h = {
34134                 tag: 'div',
34135                 cls: 'tab-header',
34136                 cn: [
34137                     {
34138                         tag: 'ul',
34139                         cls: 'nav nav-tabs pull-right',
34140                         cn: [
34141                             header
34142                         ]
34143                     }
34144                 ]
34145             };
34146         }
34147         
34148         var cfg = {
34149             tag: 'div',
34150             cls: 'nav-tabs-custom',
34151             cn: [
34152                 h,
34153                 {
34154                     tag: 'div',
34155                     cls: 'tab-content no-padding',
34156                     cn: []
34157                 }
34158             ]
34159         };
34160
34161         return  cfg;
34162     },
34163     initEvents : function()
34164     {
34165         //Roo.log('add add pane handler');
34166         this.on('addpane', this.onAddPane, this);
34167     },
34168      /**
34169      * Updates the box title
34170      * @param {String} html to set the title to.
34171      */
34172     setTitle : function(value)
34173     {
34174         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34175     },
34176     onAddPane : function(pane)
34177     {
34178         this.panes.push(pane);
34179         //Roo.log('addpane');
34180         //Roo.log(pane);
34181         // tabs are rendere left to right..
34182         if(!this.showtabs){
34183             return;
34184         }
34185         
34186         var ctr = this.el.select('.nav-tabs', true).first();
34187          
34188          
34189         var existing = ctr.select('.nav-tab',true);
34190         var qty = existing.getCount();;
34191         
34192         
34193         var tab = ctr.createChild({
34194             tag : 'li',
34195             cls : 'nav-tab' + (qty ? '' : ' active'),
34196             cn : [
34197                 {
34198                     tag : 'a',
34199                     href:'#',
34200                     html : pane.title
34201                 }
34202             ]
34203         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34204         pane.tab = tab;
34205         
34206         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34207         if (!qty) {
34208             pane.el.addClass('active');
34209         }
34210         
34211                 
34212     },
34213     onTabClick : function(ev,un,ob,pane)
34214     {
34215         //Roo.log('tab - prev default');
34216         ev.preventDefault();
34217         
34218         
34219         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34220         pane.tab.addClass('active');
34221         //Roo.log(pane.title);
34222         this.getChildContainer().select('.tab-pane',true).removeClass('active');
34223         // technically we should have a deactivate event.. but maybe add later.
34224         // and it should not de-activate the selected tab...
34225         this.fireEvent('activatepane', pane);
34226         pane.el.addClass('active');
34227         pane.fireEvent('activate');
34228         
34229         
34230     },
34231     
34232     getActivePane : function()
34233     {
34234         var r = false;
34235         Roo.each(this.panes, function(p) {
34236             if(p.el.hasClass('active')){
34237                 r = p;
34238                 return false;
34239             }
34240             
34241             return;
34242         });
34243         
34244         return r;
34245     }
34246     
34247     
34248 });
34249
34250  
34251 /*
34252  * - LGPL
34253  *
34254  * Tab pane
34255  * 
34256  */
34257 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34258 /**
34259  * @class Roo.bootstrap.TabPane
34260  * @extends Roo.bootstrap.Component
34261  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
34262  * Bootstrap TabPane class
34263  * @cfg {Boolean} active (false | true) Default false
34264  * @cfg {String} title title of panel
34265
34266  * 
34267  * @constructor
34268  * Create a new TabPane
34269  * @param {Object} config The config object
34270  */
34271
34272 Roo.bootstrap.dash.TabPane = function(config){
34273     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34274     
34275     this.addEvents({
34276         // raw events
34277         /**
34278          * @event activate
34279          * When a pane is activated
34280          * @param {Roo.bootstrap.dash.TabPane} pane
34281          */
34282         "activate" : true
34283          
34284     });
34285 };
34286
34287 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
34288     
34289     active : false,
34290     title : '',
34291     
34292     // the tabBox that this is attached to.
34293     tab : false,
34294      
34295     getAutoCreate : function() 
34296     {
34297         var cfg = {
34298             tag: 'div',
34299             cls: 'tab-pane'
34300         };
34301         
34302         if(this.active){
34303             cfg.cls += ' active';
34304         }
34305         
34306         return cfg;
34307     },
34308     initEvents  : function()
34309     {
34310         //Roo.log('trigger add pane handler');
34311         this.parent().fireEvent('addpane', this)
34312     },
34313     
34314      /**
34315      * Updates the tab title 
34316      * @param {String} html to set the title to.
34317      */
34318     setTitle: function(str)
34319     {
34320         if (!this.tab) {
34321             return;
34322         }
34323         this.title = str;
34324         this.tab.select('a', true).first().dom.innerHTML = str;
34325         
34326     }
34327     
34328     
34329     
34330 });
34331
34332  
34333
34334
34335  /*
34336  * - LGPL
34337  *
34338  * Tooltip
34339  * 
34340  */
34341
34342 /**
34343  * @class Roo.bootstrap.Tooltip
34344  * Bootstrap Tooltip class
34345  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34346  * to determine which dom element triggers the tooltip.
34347  * 
34348  * It needs to add support for additional attributes like tooltip-position
34349  * 
34350  * @constructor
34351  * Create a new Toolti
34352  * @param {Object} config The config object
34353  */
34354
34355 Roo.bootstrap.Tooltip = function(config){
34356     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34357     
34358     this.alignment = Roo.bootstrap.Tooltip.alignment;
34359     
34360     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34361         this.alignment = config.alignment;
34362     }
34363     
34364 };
34365
34366 Roo.apply(Roo.bootstrap.Tooltip, {
34367     /**
34368      * @function init initialize tooltip monitoring.
34369      * @static
34370      */
34371     currentEl : false,
34372     currentTip : false,
34373     currentRegion : false,
34374     
34375     //  init : delay?
34376     
34377     init : function()
34378     {
34379         Roo.get(document).on('mouseover', this.enter ,this);
34380         Roo.get(document).on('mouseout', this.leave, this);
34381          
34382         
34383         this.currentTip = new Roo.bootstrap.Tooltip();
34384     },
34385     
34386     enter : function(ev)
34387     {
34388         var dom = ev.getTarget();
34389         
34390         //Roo.log(['enter',dom]);
34391         var el = Roo.fly(dom);
34392         if (this.currentEl) {
34393             //Roo.log(dom);
34394             //Roo.log(this.currentEl);
34395             //Roo.log(this.currentEl.contains(dom));
34396             if (this.currentEl == el) {
34397                 return;
34398             }
34399             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34400                 return;
34401             }
34402
34403         }
34404         
34405         if (this.currentTip.el) {
34406             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34407         }    
34408         //Roo.log(ev);
34409         
34410         if(!el || el.dom == document){
34411             return;
34412         }
34413         
34414         var bindEl = el; 
34415         var pel = false;
34416         if (!el.attr('tooltip')) {
34417             pel = el.findParent("[tooltip]");
34418             if (pel) {
34419                 bindEl = Roo.get(pel);
34420             }
34421         }
34422         
34423        
34424         
34425         // you can not look for children, as if el is the body.. then everythign is the child..
34426         if (!pel && !el.attr('tooltip')) { //
34427             if (!el.select("[tooltip]").elements.length) {
34428                 return;
34429             }
34430             // is the mouse over this child...?
34431             bindEl = el.select("[tooltip]").first();
34432             var xy = ev.getXY();
34433             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34434                 //Roo.log("not in region.");
34435                 return;
34436             }
34437             //Roo.log("child element over..");
34438             
34439         }
34440         this.currentEl = el;
34441         this.currentTip.bind(bindEl);
34442         this.currentRegion = Roo.lib.Region.getRegion(dom);
34443         this.currentTip.enter();
34444         
34445     },
34446     leave : function(ev)
34447     {
34448         var dom = ev.getTarget();
34449         //Roo.log(['leave',dom]);
34450         if (!this.currentEl) {
34451             return;
34452         }
34453         
34454         
34455         if (dom != this.currentEl.dom) {
34456             return;
34457         }
34458         var xy = ev.getXY();
34459         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
34460             return;
34461         }
34462         // only activate leave if mouse cursor is outside... bounding box..
34463         
34464         
34465         
34466         
34467         if (this.currentTip) {
34468             this.currentTip.leave();
34469         }
34470         //Roo.log('clear currentEl');
34471         this.currentEl = false;
34472         
34473         
34474     },
34475     alignment : {
34476         'left' : ['r-l', [-2,0], 'right'],
34477         'right' : ['l-r', [2,0], 'left'],
34478         'bottom' : ['t-b', [0,2], 'top'],
34479         'top' : [ 'b-t', [0,-2], 'bottom']
34480     }
34481     
34482 });
34483
34484
34485 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
34486     
34487     
34488     bindEl : false,
34489     
34490     delay : null, // can be { show : 300 , hide: 500}
34491     
34492     timeout : null,
34493     
34494     hoverState : null, //???
34495     
34496     placement : 'bottom', 
34497     
34498     alignment : false,
34499     
34500     getAutoCreate : function(){
34501     
34502         var cfg = {
34503            cls : 'tooltip',   
34504            role : 'tooltip',
34505            cn : [
34506                 {
34507                     cls : 'tooltip-arrow arrow'
34508                 },
34509                 {
34510                     cls : 'tooltip-inner'
34511                 }
34512            ]
34513         };
34514         
34515         return cfg;
34516     },
34517     bind : function(el)
34518     {
34519         this.bindEl = el;
34520     },
34521     
34522     initEvents : function()
34523     {
34524         this.arrowEl = this.el.select('.arrow', true).first();
34525         this.innerEl = this.el.select('.tooltip-inner', true).first();
34526     },
34527     
34528     enter : function () {
34529        
34530         if (this.timeout != null) {
34531             clearTimeout(this.timeout);
34532         }
34533         
34534         this.hoverState = 'in';
34535          //Roo.log("enter - show");
34536         if (!this.delay || !this.delay.show) {
34537             this.show();
34538             return;
34539         }
34540         var _t = this;
34541         this.timeout = setTimeout(function () {
34542             if (_t.hoverState == 'in') {
34543                 _t.show();
34544             }
34545         }, this.delay.show);
34546     },
34547     leave : function()
34548     {
34549         clearTimeout(this.timeout);
34550     
34551         this.hoverState = 'out';
34552          if (!this.delay || !this.delay.hide) {
34553             this.hide();
34554             return;
34555         }
34556        
34557         var _t = this;
34558         this.timeout = setTimeout(function () {
34559             //Roo.log("leave - timeout");
34560             
34561             if (_t.hoverState == 'out') {
34562                 _t.hide();
34563                 Roo.bootstrap.Tooltip.currentEl = false;
34564             }
34565         }, delay);
34566     },
34567     
34568     show : function (msg)
34569     {
34570         if (!this.el) {
34571             this.render(document.body);
34572         }
34573         // set content.
34574         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34575         
34576         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34577         
34578         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34579         
34580         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34581                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34582
34583         if(this.bindEl.attr('tooltip-class')) {
34584             this.el.addClass(this.bindEl.attr('tooltip-class'));
34585         }
34586         
34587         var placement = typeof this.placement == 'function' ?
34588             this.placement.call(this, this.el, on_el) :
34589             this.placement;
34590         
34591         if(this.bindEl.attr('tooltip-placement')) {
34592             placement = this.bindEl.attr('tooltip-placement');
34593         }
34594             
34595         var autoToken = /\s?auto?\s?/i;
34596         var autoPlace = autoToken.test(placement);
34597         if (autoPlace) {
34598             placement = placement.replace(autoToken, '') || 'top';
34599         }
34600         
34601         //this.el.detach()
34602         //this.el.setXY([0,0]);
34603         this.el.show();
34604         //this.el.dom.style.display='block';
34605         
34606         //this.el.appendTo(on_el);
34607         
34608         var p = this.getPosition();
34609         var box = this.el.getBox();
34610         
34611         if (autoPlace) {
34612             // fixme..
34613         }
34614         
34615         var align = this.alignment[placement];
34616         
34617         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34618         
34619         if(placement == 'top' || placement == 'bottom'){
34620             if(xy[0] < 0){
34621                 placement = 'right';
34622             }
34623             
34624             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34625                 placement = 'left';
34626             }
34627             
34628             var scroll = Roo.select('body', true).first().getScroll();
34629             
34630             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34631                 placement = 'top';
34632             }
34633             
34634             align = this.alignment[placement];
34635             
34636             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34637             
34638         }
34639         
34640         var elems = document.getElementsByTagName('div');
34641         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34642         for (var i = 0; i < elems.length; i++) {
34643           var zindex = Number.parseInt(
34644                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34645                 10
34646           );
34647           if (zindex > highest) {
34648             highest = zindex;
34649           }
34650         }
34651         
34652         
34653         
34654         this.el.dom.style.zIndex = highest;
34655         
34656         this.el.alignTo(this.bindEl, align[0],align[1]);
34657         //var arrow = this.el.select('.arrow',true).first();
34658         //arrow.set(align[2], 
34659         
34660         this.el.addClass(placement);
34661         this.el.addClass("bs-tooltip-"+ placement);
34662         
34663         this.el.addClass('in fade show');
34664         
34665         this.hoverState = null;
34666         
34667         if (this.el.hasClass('fade')) {
34668             // fade it?
34669         }
34670         
34671         
34672         
34673         
34674         
34675     },
34676     hide : function()
34677     {
34678          
34679         if (!this.el) {
34680             return;
34681         }
34682         //this.el.setXY([0,0]);
34683         if(this.bindEl.attr('tooltip-class')) {
34684             this.el.removeClass(this.bindEl.attr('tooltip-class'));
34685         }
34686         this.el.removeClass(['show', 'in']);
34687         //this.el.hide();
34688         
34689     }
34690     
34691 });
34692  
34693
34694  /*
34695  * - LGPL
34696  *
34697  * Location Picker
34698  * 
34699  */
34700
34701 /**
34702  * @class Roo.bootstrap.LocationPicker
34703  * @extends Roo.bootstrap.Component
34704  * Bootstrap LocationPicker class
34705  * @cfg {Number} latitude Position when init default 0
34706  * @cfg {Number} longitude Position when init default 0
34707  * @cfg {Number} zoom default 15
34708  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34709  * @cfg {Boolean} mapTypeControl default false
34710  * @cfg {Boolean} disableDoubleClickZoom default false
34711  * @cfg {Boolean} scrollwheel default true
34712  * @cfg {Boolean} streetViewControl default false
34713  * @cfg {Number} radius default 0
34714  * @cfg {String} locationName
34715  * @cfg {Boolean} draggable default true
34716  * @cfg {Boolean} enableAutocomplete default false
34717  * @cfg {Boolean} enableReverseGeocode default true
34718  * @cfg {String} markerTitle
34719  * 
34720  * @constructor
34721  * Create a new LocationPicker
34722  * @param {Object} config The config object
34723  */
34724
34725
34726 Roo.bootstrap.LocationPicker = function(config){
34727     
34728     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34729     
34730     this.addEvents({
34731         /**
34732          * @event initial
34733          * Fires when the picker initialized.
34734          * @param {Roo.bootstrap.LocationPicker} this
34735          * @param {Google Location} location
34736          */
34737         initial : true,
34738         /**
34739          * @event positionchanged
34740          * Fires when the picker position changed.
34741          * @param {Roo.bootstrap.LocationPicker} this
34742          * @param {Google Location} location
34743          */
34744         positionchanged : true,
34745         /**
34746          * @event resize
34747          * Fires when the map resize.
34748          * @param {Roo.bootstrap.LocationPicker} this
34749          */
34750         resize : true,
34751         /**
34752          * @event show
34753          * Fires when the map show.
34754          * @param {Roo.bootstrap.LocationPicker} this
34755          */
34756         show : true,
34757         /**
34758          * @event hide
34759          * Fires when the map hide.
34760          * @param {Roo.bootstrap.LocationPicker} this
34761          */
34762         hide : true,
34763         /**
34764          * @event mapClick
34765          * Fires when click the map.
34766          * @param {Roo.bootstrap.LocationPicker} this
34767          * @param {Map event} e
34768          */
34769         mapClick : true,
34770         /**
34771          * @event mapRightClick
34772          * Fires when right click the map.
34773          * @param {Roo.bootstrap.LocationPicker} this
34774          * @param {Map event} e
34775          */
34776         mapRightClick : true,
34777         /**
34778          * @event markerClick
34779          * Fires when click the marker.
34780          * @param {Roo.bootstrap.LocationPicker} this
34781          * @param {Map event} e
34782          */
34783         markerClick : true,
34784         /**
34785          * @event markerRightClick
34786          * Fires when right click the marker.
34787          * @param {Roo.bootstrap.LocationPicker} this
34788          * @param {Map event} e
34789          */
34790         markerRightClick : true,
34791         /**
34792          * @event OverlayViewDraw
34793          * Fires when OverlayView Draw
34794          * @param {Roo.bootstrap.LocationPicker} this
34795          */
34796         OverlayViewDraw : true,
34797         /**
34798          * @event OverlayViewOnAdd
34799          * Fires when OverlayView Draw
34800          * @param {Roo.bootstrap.LocationPicker} this
34801          */
34802         OverlayViewOnAdd : true,
34803         /**
34804          * @event OverlayViewOnRemove
34805          * Fires when OverlayView Draw
34806          * @param {Roo.bootstrap.LocationPicker} this
34807          */
34808         OverlayViewOnRemove : true,
34809         /**
34810          * @event OverlayViewShow
34811          * Fires when OverlayView Draw
34812          * @param {Roo.bootstrap.LocationPicker} this
34813          * @param {Pixel} cpx
34814          */
34815         OverlayViewShow : true,
34816         /**
34817          * @event OverlayViewHide
34818          * Fires when OverlayView Draw
34819          * @param {Roo.bootstrap.LocationPicker} this
34820          */
34821         OverlayViewHide : true,
34822         /**
34823          * @event loadexception
34824          * Fires when load google lib failed.
34825          * @param {Roo.bootstrap.LocationPicker} this
34826          */
34827         loadexception : true
34828     });
34829         
34830 };
34831
34832 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
34833     
34834     gMapContext: false,
34835     
34836     latitude: 0,
34837     longitude: 0,
34838     zoom: 15,
34839     mapTypeId: false,
34840     mapTypeControl: false,
34841     disableDoubleClickZoom: false,
34842     scrollwheel: true,
34843     streetViewControl: false,
34844     radius: 0,
34845     locationName: '',
34846     draggable: true,
34847     enableAutocomplete: false,
34848     enableReverseGeocode: true,
34849     markerTitle: '',
34850     
34851     getAutoCreate: function()
34852     {
34853
34854         var cfg = {
34855             tag: 'div',
34856             cls: 'roo-location-picker'
34857         };
34858         
34859         return cfg
34860     },
34861     
34862     initEvents: function(ct, position)
34863     {       
34864         if(!this.el.getWidth() || this.isApplied()){
34865             return;
34866         }
34867         
34868         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34869         
34870         this.initial();
34871     },
34872     
34873     initial: function()
34874     {
34875         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34876             this.fireEvent('loadexception', this);
34877             return;
34878         }
34879         
34880         if(!this.mapTypeId){
34881             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34882         }
34883         
34884         this.gMapContext = this.GMapContext();
34885         
34886         this.initOverlayView();
34887         
34888         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34889         
34890         var _this = this;
34891                 
34892         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34893             _this.setPosition(_this.gMapContext.marker.position);
34894         });
34895         
34896         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34897             _this.fireEvent('mapClick', this, event);
34898             
34899         });
34900
34901         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34902             _this.fireEvent('mapRightClick', this, event);
34903             
34904         });
34905         
34906         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34907             _this.fireEvent('markerClick', this, event);
34908             
34909         });
34910
34911         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34912             _this.fireEvent('markerRightClick', this, event);
34913             
34914         });
34915         
34916         this.setPosition(this.gMapContext.location);
34917         
34918         this.fireEvent('initial', this, this.gMapContext.location);
34919     },
34920     
34921     initOverlayView: function()
34922     {
34923         var _this = this;
34924         
34925         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34926             
34927             draw: function()
34928             {
34929                 _this.fireEvent('OverlayViewDraw', _this);
34930             },
34931             
34932             onAdd: function()
34933             {
34934                 _this.fireEvent('OverlayViewOnAdd', _this);
34935             },
34936             
34937             onRemove: function()
34938             {
34939                 _this.fireEvent('OverlayViewOnRemove', _this);
34940             },
34941             
34942             show: function(cpx)
34943             {
34944                 _this.fireEvent('OverlayViewShow', _this, cpx);
34945             },
34946             
34947             hide: function()
34948             {
34949                 _this.fireEvent('OverlayViewHide', _this);
34950             }
34951             
34952         });
34953     },
34954     
34955     fromLatLngToContainerPixel: function(event)
34956     {
34957         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34958     },
34959     
34960     isApplied: function() 
34961     {
34962         return this.getGmapContext() == false ? false : true;
34963     },
34964     
34965     getGmapContext: function() 
34966     {
34967         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34968     },
34969     
34970     GMapContext: function() 
34971     {
34972         var position = new google.maps.LatLng(this.latitude, this.longitude);
34973         
34974         var _map = new google.maps.Map(this.el.dom, {
34975             center: position,
34976             zoom: this.zoom,
34977             mapTypeId: this.mapTypeId,
34978             mapTypeControl: this.mapTypeControl,
34979             disableDoubleClickZoom: this.disableDoubleClickZoom,
34980             scrollwheel: this.scrollwheel,
34981             streetViewControl: this.streetViewControl,
34982             locationName: this.locationName,
34983             draggable: this.draggable,
34984             enableAutocomplete: this.enableAutocomplete,
34985             enableReverseGeocode: this.enableReverseGeocode
34986         });
34987         
34988         var _marker = new google.maps.Marker({
34989             position: position,
34990             map: _map,
34991             title: this.markerTitle,
34992             draggable: this.draggable
34993         });
34994         
34995         return {
34996             map: _map,
34997             marker: _marker,
34998             circle: null,
34999             location: position,
35000             radius: this.radius,
35001             locationName: this.locationName,
35002             addressComponents: {
35003                 formatted_address: null,
35004                 addressLine1: null,
35005                 addressLine2: null,
35006                 streetName: null,
35007                 streetNumber: null,
35008                 city: null,
35009                 district: null,
35010                 state: null,
35011                 stateOrProvince: null
35012             },
35013             settings: this,
35014             domContainer: this.el.dom,
35015             geodecoder: new google.maps.Geocoder()
35016         };
35017     },
35018     
35019     drawCircle: function(center, radius, options) 
35020     {
35021         if (this.gMapContext.circle != null) {
35022             this.gMapContext.circle.setMap(null);
35023         }
35024         if (radius > 0) {
35025             radius *= 1;
35026             options = Roo.apply({}, options, {
35027                 strokeColor: "#0000FF",
35028                 strokeOpacity: .35,
35029                 strokeWeight: 2,
35030                 fillColor: "#0000FF",
35031                 fillOpacity: .2
35032             });
35033             
35034             options.map = this.gMapContext.map;
35035             options.radius = radius;
35036             options.center = center;
35037             this.gMapContext.circle = new google.maps.Circle(options);
35038             return this.gMapContext.circle;
35039         }
35040         
35041         return null;
35042     },
35043     
35044     setPosition: function(location) 
35045     {
35046         this.gMapContext.location = location;
35047         this.gMapContext.marker.setPosition(location);
35048         this.gMapContext.map.panTo(location);
35049         this.drawCircle(location, this.gMapContext.radius, {});
35050         
35051         var _this = this;
35052         
35053         if (this.gMapContext.settings.enableReverseGeocode) {
35054             this.gMapContext.geodecoder.geocode({
35055                 latLng: this.gMapContext.location
35056             }, function(results, status) {
35057                 
35058                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35059                     _this.gMapContext.locationName = results[0].formatted_address;
35060                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35061                     
35062                     _this.fireEvent('positionchanged', this, location);
35063                 }
35064             });
35065             
35066             return;
35067         }
35068         
35069         this.fireEvent('positionchanged', this, location);
35070     },
35071     
35072     resize: function()
35073     {
35074         google.maps.event.trigger(this.gMapContext.map, "resize");
35075         
35076         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35077         
35078         this.fireEvent('resize', this);
35079     },
35080     
35081     setPositionByLatLng: function(latitude, longitude)
35082     {
35083         this.setPosition(new google.maps.LatLng(latitude, longitude));
35084     },
35085     
35086     getCurrentPosition: function() 
35087     {
35088         return {
35089             latitude: this.gMapContext.location.lat(),
35090             longitude: this.gMapContext.location.lng()
35091         };
35092     },
35093     
35094     getAddressName: function() 
35095     {
35096         return this.gMapContext.locationName;
35097     },
35098     
35099     getAddressComponents: function() 
35100     {
35101         return this.gMapContext.addressComponents;
35102     },
35103     
35104     address_component_from_google_geocode: function(address_components) 
35105     {
35106         var result = {};
35107         
35108         for (var i = 0; i < address_components.length; i++) {
35109             var component = address_components[i];
35110             if (component.types.indexOf("postal_code") >= 0) {
35111                 result.postalCode = component.short_name;
35112             } else if (component.types.indexOf("street_number") >= 0) {
35113                 result.streetNumber = component.short_name;
35114             } else if (component.types.indexOf("route") >= 0) {
35115                 result.streetName = component.short_name;
35116             } else if (component.types.indexOf("neighborhood") >= 0) {
35117                 result.city = component.short_name;
35118             } else if (component.types.indexOf("locality") >= 0) {
35119                 result.city = component.short_name;
35120             } else if (component.types.indexOf("sublocality") >= 0) {
35121                 result.district = component.short_name;
35122             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35123                 result.stateOrProvince = component.short_name;
35124             } else if (component.types.indexOf("country") >= 0) {
35125                 result.country = component.short_name;
35126             }
35127         }
35128         
35129         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35130         result.addressLine2 = "";
35131         return result;
35132     },
35133     
35134     setZoomLevel: function(zoom)
35135     {
35136         this.gMapContext.map.setZoom(zoom);
35137     },
35138     
35139     show: function()
35140     {
35141         if(!this.el){
35142             return;
35143         }
35144         
35145         this.el.show();
35146         
35147         this.resize();
35148         
35149         this.fireEvent('show', this);
35150     },
35151     
35152     hide: function()
35153     {
35154         if(!this.el){
35155             return;
35156         }
35157         
35158         this.el.hide();
35159         
35160         this.fireEvent('hide', this);
35161     }
35162     
35163 });
35164
35165 Roo.apply(Roo.bootstrap.LocationPicker, {
35166     
35167     OverlayView : function(map, options)
35168     {
35169         options = options || {};
35170         
35171         this.setMap(map);
35172     }
35173     
35174     
35175 });/**
35176  * @class Roo.bootstrap.Alert
35177  * @extends Roo.bootstrap.Component
35178  * Bootstrap Alert class - shows an alert area box
35179  * eg
35180  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35181   Enter a valid email address
35182 </div>
35183  * @licence LGPL
35184  * @cfg {String} title The title of alert
35185  * @cfg {String} html The content of alert
35186  * @cfg {String} weight (success|info|warning|danger) Weight of the message
35187  * @cfg {String} fa font-awesomeicon
35188  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35189  * @cfg {Boolean} close true to show a x closer
35190  * 
35191  * 
35192  * @constructor
35193  * Create a new alert
35194  * @param {Object} config The config object
35195  */
35196
35197
35198 Roo.bootstrap.Alert = function(config){
35199     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35200     
35201 };
35202
35203 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
35204     
35205     title: '',
35206     html: '',
35207     weight: false,
35208     fa: false,
35209     faicon: false, // BC
35210     close : false,
35211     
35212     
35213     getAutoCreate : function()
35214     {
35215         
35216         var cfg = {
35217             tag : 'div',
35218             cls : 'alert',
35219             cn : [
35220                 {
35221                     tag: 'button',
35222                     type :  "button",
35223                     cls: "close",
35224                     html : '×',
35225                     style : this.close ? '' : 'display:none'
35226                 },
35227                 {
35228                     tag : 'i',
35229                     cls : 'roo-alert-icon'
35230                     
35231                 },
35232                 {
35233                     tag : 'b',
35234                     cls : 'roo-alert-title',
35235                     html : this.title
35236                 },
35237                 {
35238                     tag : 'span',
35239                     cls : 'roo-alert-text',
35240                     html : this.html
35241                 }
35242             ]
35243         };
35244         
35245         if(this.faicon){
35246             cfg.cn[0].cls += ' fa ' + this.faicon;
35247         }
35248         if(this.fa){
35249             cfg.cn[0].cls += ' fa ' + this.fa;
35250         }
35251         
35252         if(this.weight){
35253             cfg.cls += ' alert-' + this.weight;
35254         }
35255         
35256         return cfg;
35257     },
35258     
35259     initEvents: function() 
35260     {
35261         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35262         this.titleEl =  this.el.select('.roo-alert-title',true).first();
35263         this.iconEl = this.el.select('.roo-alert-icon',true).first();
35264         this.htmlEl = this.el.select('.roo-alert-text',true).first();
35265         if (this.seconds > 0) {
35266             this.hide.defer(this.seconds, this);
35267         }
35268     },
35269     /**
35270      * Set the Title Message HTML
35271      * @param {String} html
35272      */
35273     setTitle : function(str)
35274     {
35275         this.titleEl.dom.innerHTML = str;
35276     },
35277      
35278      /**
35279      * Set the Body Message HTML
35280      * @param {String} html
35281      */
35282     setHtml : function(str)
35283     {
35284         this.htmlEl.dom.innerHTML = str;
35285     },
35286     /**
35287      * Set the Weight of the alert
35288      * @param {String} (success|info|warning|danger) weight
35289      */
35290     
35291     setWeight : function(weight)
35292     {
35293         if(this.weight){
35294             this.el.removeClass('alert-' + this.weight);
35295         }
35296         
35297         this.weight = weight;
35298         
35299         this.el.addClass('alert-' + this.weight);
35300     },
35301       /**
35302      * Set the Icon of the alert
35303      * @param {String} see fontawsome names (name without the 'fa-' bit)
35304      */
35305     setIcon : function(icon)
35306     {
35307         if(this.faicon){
35308             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35309         }
35310         
35311         this.faicon = icon;
35312         
35313         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35314     },
35315     /**
35316      * Hide the Alert
35317      */
35318     hide: function() 
35319     {
35320         this.el.hide();   
35321     },
35322     /**
35323      * Show the Alert
35324      */
35325     show: function() 
35326     {  
35327         this.el.show();   
35328     }
35329     
35330 });
35331
35332  
35333 /*
35334 * Licence: LGPL
35335 */
35336
35337 /**
35338  * @class Roo.bootstrap.UploadCropbox
35339  * @extends Roo.bootstrap.Component
35340  * Bootstrap UploadCropbox class
35341  * @cfg {String} emptyText show when image has been loaded
35342  * @cfg {String} rotateNotify show when image too small to rotate
35343  * @cfg {Number} errorTimeout default 3000
35344  * @cfg {Number} minWidth default 300
35345  * @cfg {Number} minHeight default 300
35346  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35347  * @cfg {Boolean} isDocument (true|false) default false
35348  * @cfg {String} url action url
35349  * @cfg {String} paramName default 'imageUpload'
35350  * @cfg {String} method default POST
35351  * @cfg {Boolean} loadMask (true|false) default true
35352  * @cfg {Boolean} loadingText default 'Loading...'
35353  * 
35354  * @constructor
35355  * Create a new UploadCropbox
35356  * @param {Object} config The config object
35357  */
35358
35359 Roo.bootstrap.UploadCropbox = function(config){
35360     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35361     
35362     this.addEvents({
35363         /**
35364          * @event beforeselectfile
35365          * Fire before select file
35366          * @param {Roo.bootstrap.UploadCropbox} this
35367          */
35368         "beforeselectfile" : true,
35369         /**
35370          * @event initial
35371          * Fire after initEvent
35372          * @param {Roo.bootstrap.UploadCropbox} this
35373          */
35374         "initial" : true,
35375         /**
35376          * @event crop
35377          * Fire after initEvent
35378          * @param {Roo.bootstrap.UploadCropbox} this
35379          * @param {String} data
35380          */
35381         "crop" : true,
35382         /**
35383          * @event prepare
35384          * Fire when preparing the file data
35385          * @param {Roo.bootstrap.UploadCropbox} this
35386          * @param {Object} file
35387          */
35388         "prepare" : true,
35389         /**
35390          * @event exception
35391          * Fire when get exception
35392          * @param {Roo.bootstrap.UploadCropbox} this
35393          * @param {XMLHttpRequest} xhr
35394          */
35395         "exception" : true,
35396         /**
35397          * @event beforeloadcanvas
35398          * Fire before load the canvas
35399          * @param {Roo.bootstrap.UploadCropbox} this
35400          * @param {String} src
35401          */
35402         "beforeloadcanvas" : true,
35403         /**
35404          * @event trash
35405          * Fire when trash image
35406          * @param {Roo.bootstrap.UploadCropbox} this
35407          */
35408         "trash" : true,
35409         /**
35410          * @event download
35411          * Fire when download the image
35412          * @param {Roo.bootstrap.UploadCropbox} this
35413          */
35414         "download" : true,
35415         /**
35416          * @event footerbuttonclick
35417          * Fire when footerbuttonclick
35418          * @param {Roo.bootstrap.UploadCropbox} this
35419          * @param {String} type
35420          */
35421         "footerbuttonclick" : true,
35422         /**
35423          * @event resize
35424          * Fire when resize
35425          * @param {Roo.bootstrap.UploadCropbox} this
35426          */
35427         "resize" : true,
35428         /**
35429          * @event rotate
35430          * Fire when rotate the image
35431          * @param {Roo.bootstrap.UploadCropbox} this
35432          * @param {String} pos
35433          */
35434         "rotate" : true,
35435         /**
35436          * @event inspect
35437          * Fire when inspect the file
35438          * @param {Roo.bootstrap.UploadCropbox} this
35439          * @param {Object} file
35440          */
35441         "inspect" : true,
35442         /**
35443          * @event upload
35444          * Fire when xhr upload the file
35445          * @param {Roo.bootstrap.UploadCropbox} this
35446          * @param {Object} data
35447          */
35448         "upload" : true,
35449         /**
35450          * @event arrange
35451          * Fire when arrange the file data
35452          * @param {Roo.bootstrap.UploadCropbox} this
35453          * @param {Object} formData
35454          */
35455         "arrange" : true
35456     });
35457     
35458     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35459 };
35460
35461 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
35462     
35463     emptyText : 'Click to upload image',
35464     rotateNotify : 'Image is too small to rotate',
35465     errorTimeout : 3000,
35466     scale : 0,
35467     baseScale : 1,
35468     rotate : 0,
35469     dragable : false,
35470     pinching : false,
35471     mouseX : 0,
35472     mouseY : 0,
35473     cropData : false,
35474     minWidth : 300,
35475     minHeight : 300,
35476     file : false,
35477     exif : {},
35478     baseRotate : 1,
35479     cropType : 'image/jpeg',
35480     buttons : false,
35481     canvasLoaded : false,
35482     isDocument : false,
35483     method : 'POST',
35484     paramName : 'imageUpload',
35485     loadMask : true,
35486     loadingText : 'Loading...',
35487     maskEl : false,
35488     
35489     getAutoCreate : function()
35490     {
35491         var cfg = {
35492             tag : 'div',
35493             cls : 'roo-upload-cropbox',
35494             cn : [
35495                 {
35496                     tag : 'input',
35497                     cls : 'roo-upload-cropbox-selector',
35498                     type : 'file'
35499                 },
35500                 {
35501                     tag : 'div',
35502                     cls : 'roo-upload-cropbox-body',
35503                     style : 'cursor:pointer',
35504                     cn : [
35505                         {
35506                             tag : 'div',
35507                             cls : 'roo-upload-cropbox-preview'
35508                         },
35509                         {
35510                             tag : 'div',
35511                             cls : 'roo-upload-cropbox-thumb'
35512                         },
35513                         {
35514                             tag : 'div',
35515                             cls : 'roo-upload-cropbox-empty-notify',
35516                             html : this.emptyText
35517                         },
35518                         {
35519                             tag : 'div',
35520                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35521                             html : this.rotateNotify
35522                         }
35523                     ]
35524                 },
35525                 {
35526                     tag : 'div',
35527                     cls : 'roo-upload-cropbox-footer',
35528                     cn : {
35529                         tag : 'div',
35530                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35531                         cn : []
35532                     }
35533                 }
35534             ]
35535         };
35536         
35537         return cfg;
35538     },
35539     
35540     onRender : function(ct, position)
35541     {
35542         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35543         
35544         if (this.buttons.length) {
35545             
35546             Roo.each(this.buttons, function(bb) {
35547                 
35548                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35549                 
35550                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35551                 
35552             }, this);
35553         }
35554         
35555         if(this.loadMask){
35556             this.maskEl = this.el;
35557         }
35558     },
35559     
35560     initEvents : function()
35561     {
35562         this.urlAPI = (window.createObjectURL && window) || 
35563                                 (window.URL && URL.revokeObjectURL && URL) || 
35564                                 (window.webkitURL && webkitURL);
35565                         
35566         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35567         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35568         
35569         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35570         this.selectorEl.hide();
35571         
35572         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35573         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35574         
35575         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35576         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35577         this.thumbEl.hide();
35578         
35579         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35580         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35581         
35582         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35583         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35584         this.errorEl.hide();
35585         
35586         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35587         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35588         this.footerEl.hide();
35589         
35590         this.setThumbBoxSize();
35591         
35592         this.bind();
35593         
35594         this.resize();
35595         
35596         this.fireEvent('initial', this);
35597     },
35598
35599     bind : function()
35600     {
35601         var _this = this;
35602         
35603         window.addEventListener("resize", function() { _this.resize(); } );
35604         
35605         this.bodyEl.on('click', this.beforeSelectFile, this);
35606         
35607         if(Roo.isTouch){
35608             this.bodyEl.on('touchstart', this.onTouchStart, this);
35609             this.bodyEl.on('touchmove', this.onTouchMove, this);
35610             this.bodyEl.on('touchend', this.onTouchEnd, this);
35611         }
35612         
35613         if(!Roo.isTouch){
35614             this.bodyEl.on('mousedown', this.onMouseDown, this);
35615             this.bodyEl.on('mousemove', this.onMouseMove, this);
35616             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35617             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35618             Roo.get(document).on('mouseup', this.onMouseUp, this);
35619         }
35620         
35621         this.selectorEl.on('change', this.onFileSelected, this);
35622     },
35623     
35624     reset : function()
35625     {    
35626         this.scale = 0;
35627         this.baseScale = 1;
35628         this.rotate = 0;
35629         this.baseRotate = 1;
35630         this.dragable = false;
35631         this.pinching = false;
35632         this.mouseX = 0;
35633         this.mouseY = 0;
35634         this.cropData = false;
35635         this.notifyEl.dom.innerHTML = this.emptyText;
35636         
35637         this.selectorEl.dom.value = '';
35638         
35639     },
35640     
35641     resize : function()
35642     {
35643         if(this.fireEvent('resize', this) != false){
35644             this.setThumbBoxPosition();
35645             this.setCanvasPosition();
35646         }
35647     },
35648     
35649     onFooterButtonClick : function(e, el, o, type)
35650     {
35651         switch (type) {
35652             case 'rotate-left' :
35653                 this.onRotateLeft(e);
35654                 break;
35655             case 'rotate-right' :
35656                 this.onRotateRight(e);
35657                 break;
35658             case 'picture' :
35659                 this.beforeSelectFile(e);
35660                 break;
35661             case 'trash' :
35662                 this.trash(e);
35663                 break;
35664             case 'crop' :
35665                 this.crop(e);
35666                 break;
35667             case 'download' :
35668                 this.download(e);
35669                 break;
35670             default :
35671                 break;
35672         }
35673         
35674         this.fireEvent('footerbuttonclick', this, type);
35675     },
35676     
35677     beforeSelectFile : function(e)
35678     {
35679         e.preventDefault();
35680         
35681         if(this.fireEvent('beforeselectfile', this) != false){
35682             this.selectorEl.dom.click();
35683         }
35684     },
35685     
35686     onFileSelected : function(e)
35687     {
35688         e.preventDefault();
35689         
35690         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35691             return;
35692         }
35693         
35694         var file = this.selectorEl.dom.files[0];
35695         
35696         if(this.fireEvent('inspect', this, file) != false){
35697             this.prepare(file);
35698         }
35699         
35700     },
35701     
35702     trash : function(e)
35703     {
35704         this.fireEvent('trash', this);
35705     },
35706     
35707     download : function(e)
35708     {
35709         this.fireEvent('download', this);
35710     },
35711     
35712     loadCanvas : function(src)
35713     {   
35714         if(this.fireEvent('beforeloadcanvas', this, src) != false){
35715             
35716             this.reset();
35717             
35718             this.imageEl = document.createElement('img');
35719             
35720             var _this = this;
35721             
35722             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35723             
35724             this.imageEl.src = src;
35725         }
35726     },
35727     
35728     onLoadCanvas : function()
35729     {   
35730         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35731         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35732         
35733         this.bodyEl.un('click', this.beforeSelectFile, this);
35734         
35735         this.notifyEl.hide();
35736         this.thumbEl.show();
35737         this.footerEl.show();
35738         
35739         this.baseRotateLevel();
35740         
35741         if(this.isDocument){
35742             this.setThumbBoxSize();
35743         }
35744         
35745         this.setThumbBoxPosition();
35746         
35747         this.baseScaleLevel();
35748         
35749         this.draw();
35750         
35751         this.resize();
35752         
35753         this.canvasLoaded = true;
35754         
35755         if(this.loadMask){
35756             this.maskEl.unmask();
35757         }
35758         
35759     },
35760     
35761     setCanvasPosition : function()
35762     {   
35763         if(!this.canvasEl){
35764             return;
35765         }
35766         
35767         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35768         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35769         
35770         this.previewEl.setLeft(pw);
35771         this.previewEl.setTop(ph);
35772         
35773     },
35774     
35775     onMouseDown : function(e)
35776     {   
35777         e.stopEvent();
35778         
35779         this.dragable = true;
35780         this.pinching = false;
35781         
35782         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35783             this.dragable = false;
35784             return;
35785         }
35786         
35787         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35788         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35789         
35790     },
35791     
35792     onMouseMove : function(e)
35793     {   
35794         e.stopEvent();
35795         
35796         if(!this.canvasLoaded){
35797             return;
35798         }
35799         
35800         if (!this.dragable){
35801             return;
35802         }
35803         
35804         var minX = Math.ceil(this.thumbEl.getLeft(true));
35805         var minY = Math.ceil(this.thumbEl.getTop(true));
35806         
35807         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35808         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35809         
35810         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35811         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35812         
35813         x = x - this.mouseX;
35814         y = y - this.mouseY;
35815         
35816         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35817         var bgY = Math.ceil(y + this.previewEl.getTop(true));
35818         
35819         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35820         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35821         
35822         this.previewEl.setLeft(bgX);
35823         this.previewEl.setTop(bgY);
35824         
35825         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35826         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35827     },
35828     
35829     onMouseUp : function(e)
35830     {   
35831         e.stopEvent();
35832         
35833         this.dragable = false;
35834     },
35835     
35836     onMouseWheel : function(e)
35837     {   
35838         e.stopEvent();
35839         
35840         this.startScale = this.scale;
35841         
35842         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35843         
35844         if(!this.zoomable()){
35845             this.scale = this.startScale;
35846             return;
35847         }
35848         
35849         this.draw();
35850         
35851         return;
35852     },
35853     
35854     zoomable : function()
35855     {
35856         var minScale = this.thumbEl.getWidth() / this.minWidth;
35857         
35858         if(this.minWidth < this.minHeight){
35859             minScale = this.thumbEl.getHeight() / this.minHeight;
35860         }
35861         
35862         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35863         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35864         
35865         if(
35866                 this.isDocument &&
35867                 (this.rotate == 0 || this.rotate == 180) && 
35868                 (
35869                     width > this.imageEl.OriginWidth || 
35870                     height > this.imageEl.OriginHeight ||
35871                     (width < this.minWidth && height < this.minHeight)
35872                 )
35873         ){
35874             return false;
35875         }
35876         
35877         if(
35878                 this.isDocument &&
35879                 (this.rotate == 90 || this.rotate == 270) && 
35880                 (
35881                     width > this.imageEl.OriginWidth || 
35882                     height > this.imageEl.OriginHeight ||
35883                     (width < this.minHeight && height < this.minWidth)
35884                 )
35885         ){
35886             return false;
35887         }
35888         
35889         if(
35890                 !this.isDocument &&
35891                 (this.rotate == 0 || this.rotate == 180) && 
35892                 (
35893                     width < this.minWidth || 
35894                     width > this.imageEl.OriginWidth || 
35895                     height < this.minHeight || 
35896                     height > this.imageEl.OriginHeight
35897                 )
35898         ){
35899             return false;
35900         }
35901         
35902         if(
35903                 !this.isDocument &&
35904                 (this.rotate == 90 || this.rotate == 270) && 
35905                 (
35906                     width < this.minHeight || 
35907                     width > this.imageEl.OriginWidth || 
35908                     height < this.minWidth || 
35909                     height > this.imageEl.OriginHeight
35910                 )
35911         ){
35912             return false;
35913         }
35914         
35915         return true;
35916         
35917     },
35918     
35919     onRotateLeft : function(e)
35920     {   
35921         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35922             
35923             var minScale = this.thumbEl.getWidth() / this.minWidth;
35924             
35925             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35926             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35927             
35928             this.startScale = this.scale;
35929             
35930             while (this.getScaleLevel() < minScale){
35931             
35932                 this.scale = this.scale + 1;
35933                 
35934                 if(!this.zoomable()){
35935                     break;
35936                 }
35937                 
35938                 if(
35939                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35940                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35941                 ){
35942                     continue;
35943                 }
35944                 
35945                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35946
35947                 this.draw();
35948                 
35949                 return;
35950             }
35951             
35952             this.scale = this.startScale;
35953             
35954             this.onRotateFail();
35955             
35956             return false;
35957         }
35958         
35959         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35960
35961         if(this.isDocument){
35962             this.setThumbBoxSize();
35963             this.setThumbBoxPosition();
35964             this.setCanvasPosition();
35965         }
35966         
35967         this.draw();
35968         
35969         this.fireEvent('rotate', this, 'left');
35970         
35971     },
35972     
35973     onRotateRight : function(e)
35974     {
35975         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35976             
35977             var minScale = this.thumbEl.getWidth() / this.minWidth;
35978         
35979             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35980             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35981             
35982             this.startScale = this.scale;
35983             
35984             while (this.getScaleLevel() < minScale){
35985             
35986                 this.scale = this.scale + 1;
35987                 
35988                 if(!this.zoomable()){
35989                     break;
35990                 }
35991                 
35992                 if(
35993                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35994                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35995                 ){
35996                     continue;
35997                 }
35998                 
35999                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36000
36001                 this.draw();
36002                 
36003                 return;
36004             }
36005             
36006             this.scale = this.startScale;
36007             
36008             this.onRotateFail();
36009             
36010             return false;
36011         }
36012         
36013         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36014
36015         if(this.isDocument){
36016             this.setThumbBoxSize();
36017             this.setThumbBoxPosition();
36018             this.setCanvasPosition();
36019         }
36020         
36021         this.draw();
36022         
36023         this.fireEvent('rotate', this, 'right');
36024     },
36025     
36026     onRotateFail : function()
36027     {
36028         this.errorEl.show(true);
36029         
36030         var _this = this;
36031         
36032         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36033     },
36034     
36035     draw : function()
36036     {
36037         this.previewEl.dom.innerHTML = '';
36038         
36039         var canvasEl = document.createElement("canvas");
36040         
36041         var contextEl = canvasEl.getContext("2d");
36042         
36043         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36044         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36045         var center = this.imageEl.OriginWidth / 2;
36046         
36047         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36048             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36049             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36050             center = this.imageEl.OriginHeight / 2;
36051         }
36052         
36053         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36054         
36055         contextEl.translate(center, center);
36056         contextEl.rotate(this.rotate * Math.PI / 180);
36057
36058         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36059         
36060         this.canvasEl = document.createElement("canvas");
36061         
36062         this.contextEl = this.canvasEl.getContext("2d");
36063         
36064         switch (this.rotate) {
36065             case 0 :
36066                 
36067                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36068                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36069                 
36070                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36071                 
36072                 break;
36073             case 90 : 
36074                 
36075                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36076                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36077                 
36078                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36079                     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);
36080                     break;
36081                 }
36082                 
36083                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36084                 
36085                 break;
36086             case 180 :
36087                 
36088                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36089                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36090                 
36091                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36092                     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);
36093                     break;
36094                 }
36095                 
36096                 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);
36097                 
36098                 break;
36099             case 270 :
36100                 
36101                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36102                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36103         
36104                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36105                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36106                     break;
36107                 }
36108                 
36109                 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);
36110                 
36111                 break;
36112             default : 
36113                 break;
36114         }
36115         
36116         this.previewEl.appendChild(this.canvasEl);
36117         
36118         this.setCanvasPosition();
36119     },
36120     
36121     crop : function()
36122     {
36123         if(!this.canvasLoaded){
36124             return;
36125         }
36126         
36127         var imageCanvas = document.createElement("canvas");
36128         
36129         var imageContext = imageCanvas.getContext("2d");
36130         
36131         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36132         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36133         
36134         var center = imageCanvas.width / 2;
36135         
36136         imageContext.translate(center, center);
36137         
36138         imageContext.rotate(this.rotate * Math.PI / 180);
36139         
36140         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36141         
36142         var canvas = document.createElement("canvas");
36143         
36144         var context = canvas.getContext("2d");
36145                 
36146         canvas.width = this.minWidth;
36147         canvas.height = this.minHeight;
36148
36149         switch (this.rotate) {
36150             case 0 :
36151                 
36152                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36153                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36154                 
36155                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36156                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36157                 
36158                 var targetWidth = this.minWidth - 2 * x;
36159                 var targetHeight = this.minHeight - 2 * y;
36160                 
36161                 var scale = 1;
36162                 
36163                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36164                     scale = targetWidth / width;
36165                 }
36166                 
36167                 if(x > 0 && y == 0){
36168                     scale = targetHeight / height;
36169                 }
36170                 
36171                 if(x > 0 && y > 0){
36172                     scale = targetWidth / width;
36173                     
36174                     if(width < height){
36175                         scale = targetHeight / height;
36176                     }
36177                 }
36178                 
36179                 context.scale(scale, scale);
36180                 
36181                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36182                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36183
36184                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36185                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36186
36187                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36188                 
36189                 break;
36190             case 90 : 
36191                 
36192                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36193                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36194                 
36195                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36196                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36197                 
36198                 var targetWidth = this.minWidth - 2 * x;
36199                 var targetHeight = this.minHeight - 2 * y;
36200                 
36201                 var scale = 1;
36202                 
36203                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36204                     scale = targetWidth / width;
36205                 }
36206                 
36207                 if(x > 0 && y == 0){
36208                     scale = targetHeight / height;
36209                 }
36210                 
36211                 if(x > 0 && y > 0){
36212                     scale = targetWidth / width;
36213                     
36214                     if(width < height){
36215                         scale = targetHeight / height;
36216                     }
36217                 }
36218                 
36219                 context.scale(scale, scale);
36220                 
36221                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36222                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36223
36224                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36225                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36226                 
36227                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36228                 
36229                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36230                 
36231                 break;
36232             case 180 :
36233                 
36234                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36235                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36236                 
36237                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36238                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36239                 
36240                 var targetWidth = this.minWidth - 2 * x;
36241                 var targetHeight = this.minHeight - 2 * y;
36242                 
36243                 var scale = 1;
36244                 
36245                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36246                     scale = targetWidth / width;
36247                 }
36248                 
36249                 if(x > 0 && y == 0){
36250                     scale = targetHeight / height;
36251                 }
36252                 
36253                 if(x > 0 && y > 0){
36254                     scale = targetWidth / width;
36255                     
36256                     if(width < height){
36257                         scale = targetHeight / height;
36258                     }
36259                 }
36260                 
36261                 context.scale(scale, scale);
36262                 
36263                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36264                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36265
36266                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36267                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36268
36269                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36270                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36271                 
36272                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36273                 
36274                 break;
36275             case 270 :
36276                 
36277                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36278                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36279                 
36280                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36281                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36282                 
36283                 var targetWidth = this.minWidth - 2 * x;
36284                 var targetHeight = this.minHeight - 2 * y;
36285                 
36286                 var scale = 1;
36287                 
36288                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36289                     scale = targetWidth / width;
36290                 }
36291                 
36292                 if(x > 0 && y == 0){
36293                     scale = targetHeight / height;
36294                 }
36295                 
36296                 if(x > 0 && y > 0){
36297                     scale = targetWidth / width;
36298                     
36299                     if(width < height){
36300                         scale = targetHeight / height;
36301                     }
36302                 }
36303                 
36304                 context.scale(scale, scale);
36305                 
36306                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36307                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36308
36309                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36310                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36311                 
36312                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36313                 
36314                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36315                 
36316                 break;
36317             default : 
36318                 break;
36319         }
36320         
36321         this.cropData = canvas.toDataURL(this.cropType);
36322         
36323         if(this.fireEvent('crop', this, this.cropData) !== false){
36324             this.process(this.file, this.cropData);
36325         }
36326         
36327         return;
36328         
36329     },
36330     
36331     setThumbBoxSize : function()
36332     {
36333         var width, height;
36334         
36335         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36336             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36337             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36338             
36339             this.minWidth = width;
36340             this.minHeight = height;
36341             
36342             if(this.rotate == 90 || this.rotate == 270){
36343                 this.minWidth = height;
36344                 this.minHeight = width;
36345             }
36346         }
36347         
36348         height = 300;
36349         width = Math.ceil(this.minWidth * height / this.minHeight);
36350         
36351         if(this.minWidth > this.minHeight){
36352             width = 300;
36353             height = Math.ceil(this.minHeight * width / this.minWidth);
36354         }
36355         
36356         this.thumbEl.setStyle({
36357             width : width + 'px',
36358             height : height + 'px'
36359         });
36360
36361         return;
36362             
36363     },
36364     
36365     setThumbBoxPosition : function()
36366     {
36367         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36368         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36369         
36370         this.thumbEl.setLeft(x);
36371         this.thumbEl.setTop(y);
36372         
36373     },
36374     
36375     baseRotateLevel : function()
36376     {
36377         this.baseRotate = 1;
36378         
36379         if(
36380                 typeof(this.exif) != 'undefined' &&
36381                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36382                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36383         ){
36384             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36385         }
36386         
36387         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36388         
36389     },
36390     
36391     baseScaleLevel : function()
36392     {
36393         var width, height;
36394         
36395         if(this.isDocument){
36396             
36397             if(this.baseRotate == 6 || this.baseRotate == 8){
36398             
36399                 height = this.thumbEl.getHeight();
36400                 this.baseScale = height / this.imageEl.OriginWidth;
36401
36402                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36403                     width = this.thumbEl.getWidth();
36404                     this.baseScale = width / this.imageEl.OriginHeight;
36405                 }
36406
36407                 return;
36408             }
36409
36410             height = this.thumbEl.getHeight();
36411             this.baseScale = height / this.imageEl.OriginHeight;
36412
36413             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36414                 width = this.thumbEl.getWidth();
36415                 this.baseScale = width / this.imageEl.OriginWidth;
36416             }
36417
36418             return;
36419         }
36420         
36421         if(this.baseRotate == 6 || this.baseRotate == 8){
36422             
36423             width = this.thumbEl.getHeight();
36424             this.baseScale = width / this.imageEl.OriginHeight;
36425             
36426             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36427                 height = this.thumbEl.getWidth();
36428                 this.baseScale = height / this.imageEl.OriginHeight;
36429             }
36430             
36431             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36432                 height = this.thumbEl.getWidth();
36433                 this.baseScale = height / this.imageEl.OriginHeight;
36434                 
36435                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36436                     width = this.thumbEl.getHeight();
36437                     this.baseScale = width / this.imageEl.OriginWidth;
36438                 }
36439             }
36440             
36441             return;
36442         }
36443         
36444         width = this.thumbEl.getWidth();
36445         this.baseScale = width / this.imageEl.OriginWidth;
36446         
36447         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36448             height = this.thumbEl.getHeight();
36449             this.baseScale = height / this.imageEl.OriginHeight;
36450         }
36451         
36452         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36453             
36454             height = this.thumbEl.getHeight();
36455             this.baseScale = height / this.imageEl.OriginHeight;
36456             
36457             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36458                 width = this.thumbEl.getWidth();
36459                 this.baseScale = width / this.imageEl.OriginWidth;
36460             }
36461             
36462         }
36463         
36464         return;
36465     },
36466     
36467     getScaleLevel : function()
36468     {
36469         return this.baseScale * Math.pow(1.1, this.scale);
36470     },
36471     
36472     onTouchStart : function(e)
36473     {
36474         if(!this.canvasLoaded){
36475             this.beforeSelectFile(e);
36476             return;
36477         }
36478         
36479         var touches = e.browserEvent.touches;
36480         
36481         if(!touches){
36482             return;
36483         }
36484         
36485         if(touches.length == 1){
36486             this.onMouseDown(e);
36487             return;
36488         }
36489         
36490         if(touches.length != 2){
36491             return;
36492         }
36493         
36494         var coords = [];
36495         
36496         for(var i = 0, finger; finger = touches[i]; i++){
36497             coords.push(finger.pageX, finger.pageY);
36498         }
36499         
36500         var x = Math.pow(coords[0] - coords[2], 2);
36501         var y = Math.pow(coords[1] - coords[3], 2);
36502         
36503         this.startDistance = Math.sqrt(x + y);
36504         
36505         this.startScale = this.scale;
36506         
36507         this.pinching = true;
36508         this.dragable = false;
36509         
36510     },
36511     
36512     onTouchMove : function(e)
36513     {
36514         if(!this.pinching && !this.dragable){
36515             return;
36516         }
36517         
36518         var touches = e.browserEvent.touches;
36519         
36520         if(!touches){
36521             return;
36522         }
36523         
36524         if(this.dragable){
36525             this.onMouseMove(e);
36526             return;
36527         }
36528         
36529         var coords = [];
36530         
36531         for(var i = 0, finger; finger = touches[i]; i++){
36532             coords.push(finger.pageX, finger.pageY);
36533         }
36534         
36535         var x = Math.pow(coords[0] - coords[2], 2);
36536         var y = Math.pow(coords[1] - coords[3], 2);
36537         
36538         this.endDistance = Math.sqrt(x + y);
36539         
36540         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36541         
36542         if(!this.zoomable()){
36543             this.scale = this.startScale;
36544             return;
36545         }
36546         
36547         this.draw();
36548         
36549     },
36550     
36551     onTouchEnd : function(e)
36552     {
36553         this.pinching = false;
36554         this.dragable = false;
36555         
36556     },
36557     
36558     process : function(file, crop)
36559     {
36560         if(this.loadMask){
36561             this.maskEl.mask(this.loadingText);
36562         }
36563         
36564         this.xhr = new XMLHttpRequest();
36565         
36566         file.xhr = this.xhr;
36567
36568         this.xhr.open(this.method, this.url, true);
36569         
36570         var headers = {
36571             "Accept": "application/json",
36572             "Cache-Control": "no-cache",
36573             "X-Requested-With": "XMLHttpRequest"
36574         };
36575         
36576         for (var headerName in headers) {
36577             var headerValue = headers[headerName];
36578             if (headerValue) {
36579                 this.xhr.setRequestHeader(headerName, headerValue);
36580             }
36581         }
36582         
36583         var _this = this;
36584         
36585         this.xhr.onload = function()
36586         {
36587             _this.xhrOnLoad(_this.xhr);
36588         }
36589         
36590         this.xhr.onerror = function()
36591         {
36592             _this.xhrOnError(_this.xhr);
36593         }
36594         
36595         var formData = new FormData();
36596
36597         formData.append('returnHTML', 'NO');
36598         
36599         if(crop){
36600             formData.append('crop', crop);
36601         }
36602         
36603         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36604             formData.append(this.paramName, file, file.name);
36605         }
36606         
36607         if(typeof(file.filename) != 'undefined'){
36608             formData.append('filename', file.filename);
36609         }
36610         
36611         if(typeof(file.mimetype) != 'undefined'){
36612             formData.append('mimetype', file.mimetype);
36613         }
36614         
36615         if(this.fireEvent('arrange', this, formData) != false){
36616             this.xhr.send(formData);
36617         };
36618     },
36619     
36620     xhrOnLoad : function(xhr)
36621     {
36622         if(this.loadMask){
36623             this.maskEl.unmask();
36624         }
36625         
36626         if (xhr.readyState !== 4) {
36627             this.fireEvent('exception', this, xhr);
36628             return;
36629         }
36630
36631         var response = Roo.decode(xhr.responseText);
36632         
36633         if(!response.success){
36634             this.fireEvent('exception', this, xhr);
36635             return;
36636         }
36637         
36638         var response = Roo.decode(xhr.responseText);
36639         
36640         this.fireEvent('upload', this, response);
36641         
36642     },
36643     
36644     xhrOnError : function()
36645     {
36646         if(this.loadMask){
36647             this.maskEl.unmask();
36648         }
36649         
36650         Roo.log('xhr on error');
36651         
36652         var response = Roo.decode(xhr.responseText);
36653           
36654         Roo.log(response);
36655         
36656     },
36657     
36658     prepare : function(file)
36659     {   
36660         if(this.loadMask){
36661             this.maskEl.mask(this.loadingText);
36662         }
36663         
36664         this.file = false;
36665         this.exif = {};
36666         
36667         if(typeof(file) === 'string'){
36668             this.loadCanvas(file);
36669             return;
36670         }
36671         
36672         if(!file || !this.urlAPI){
36673             return;
36674         }
36675         
36676         this.file = file;
36677         this.cropType = file.type;
36678         
36679         var _this = this;
36680         
36681         if(this.fireEvent('prepare', this, this.file) != false){
36682             
36683             var reader = new FileReader();
36684             
36685             reader.onload = function (e) {
36686                 if (e.target.error) {
36687                     Roo.log(e.target.error);
36688                     return;
36689                 }
36690                 
36691                 var buffer = e.target.result,
36692                     dataView = new DataView(buffer),
36693                     offset = 2,
36694                     maxOffset = dataView.byteLength - 4,
36695                     markerBytes,
36696                     markerLength;
36697                 
36698                 if (dataView.getUint16(0) === 0xffd8) {
36699                     while (offset < maxOffset) {
36700                         markerBytes = dataView.getUint16(offset);
36701                         
36702                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36703                             markerLength = dataView.getUint16(offset + 2) + 2;
36704                             if (offset + markerLength > dataView.byteLength) {
36705                                 Roo.log('Invalid meta data: Invalid segment size.');
36706                                 break;
36707                             }
36708                             
36709                             if(markerBytes == 0xffe1){
36710                                 _this.parseExifData(
36711                                     dataView,
36712                                     offset,
36713                                     markerLength
36714                                 );
36715                             }
36716                             
36717                             offset += markerLength;
36718                             
36719                             continue;
36720                         }
36721                         
36722                         break;
36723                     }
36724                     
36725                 }
36726                 
36727                 var url = _this.urlAPI.createObjectURL(_this.file);
36728                 
36729                 _this.loadCanvas(url);
36730                 
36731                 return;
36732             }
36733             
36734             reader.readAsArrayBuffer(this.file);
36735             
36736         }
36737         
36738     },
36739     
36740     parseExifData : function(dataView, offset, length)
36741     {
36742         var tiffOffset = offset + 10,
36743             littleEndian,
36744             dirOffset;
36745     
36746         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36747             // No Exif data, might be XMP data instead
36748             return;
36749         }
36750         
36751         // Check for the ASCII code for "Exif" (0x45786966):
36752         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36753             // No Exif data, might be XMP data instead
36754             return;
36755         }
36756         if (tiffOffset + 8 > dataView.byteLength) {
36757             Roo.log('Invalid Exif data: Invalid segment size.');
36758             return;
36759         }
36760         // Check for the two null bytes:
36761         if (dataView.getUint16(offset + 8) !== 0x0000) {
36762             Roo.log('Invalid Exif data: Missing byte alignment offset.');
36763             return;
36764         }
36765         // Check the byte alignment:
36766         switch (dataView.getUint16(tiffOffset)) {
36767         case 0x4949:
36768             littleEndian = true;
36769             break;
36770         case 0x4D4D:
36771             littleEndian = false;
36772             break;
36773         default:
36774             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36775             return;
36776         }
36777         // Check for the TIFF tag marker (0x002A):
36778         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36779             Roo.log('Invalid Exif data: Missing TIFF marker.');
36780             return;
36781         }
36782         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36783         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36784         
36785         this.parseExifTags(
36786             dataView,
36787             tiffOffset,
36788             tiffOffset + dirOffset,
36789             littleEndian
36790         );
36791     },
36792     
36793     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36794     {
36795         var tagsNumber,
36796             dirEndOffset,
36797             i;
36798         if (dirOffset + 6 > dataView.byteLength) {
36799             Roo.log('Invalid Exif data: Invalid directory offset.');
36800             return;
36801         }
36802         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36803         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36804         if (dirEndOffset + 4 > dataView.byteLength) {
36805             Roo.log('Invalid Exif data: Invalid directory size.');
36806             return;
36807         }
36808         for (i = 0; i < tagsNumber; i += 1) {
36809             this.parseExifTag(
36810                 dataView,
36811                 tiffOffset,
36812                 dirOffset + 2 + 12 * i, // tag offset
36813                 littleEndian
36814             );
36815         }
36816         // Return the offset to the next directory:
36817         return dataView.getUint32(dirEndOffset, littleEndian);
36818     },
36819     
36820     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
36821     {
36822         var tag = dataView.getUint16(offset, littleEndian);
36823         
36824         this.exif[tag] = this.getExifValue(
36825             dataView,
36826             tiffOffset,
36827             offset,
36828             dataView.getUint16(offset + 2, littleEndian), // tag type
36829             dataView.getUint32(offset + 4, littleEndian), // tag length
36830             littleEndian
36831         );
36832     },
36833     
36834     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36835     {
36836         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36837             tagSize,
36838             dataOffset,
36839             values,
36840             i,
36841             str,
36842             c;
36843     
36844         if (!tagType) {
36845             Roo.log('Invalid Exif data: Invalid tag type.');
36846             return;
36847         }
36848         
36849         tagSize = tagType.size * length;
36850         // Determine if the value is contained in the dataOffset bytes,
36851         // or if the value at the dataOffset is a pointer to the actual data:
36852         dataOffset = tagSize > 4 ?
36853                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36854         if (dataOffset + tagSize > dataView.byteLength) {
36855             Roo.log('Invalid Exif data: Invalid data offset.');
36856             return;
36857         }
36858         if (length === 1) {
36859             return tagType.getValue(dataView, dataOffset, littleEndian);
36860         }
36861         values = [];
36862         for (i = 0; i < length; i += 1) {
36863             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36864         }
36865         
36866         if (tagType.ascii) {
36867             str = '';
36868             // Concatenate the chars:
36869             for (i = 0; i < values.length; i += 1) {
36870                 c = values[i];
36871                 // Ignore the terminating NULL byte(s):
36872                 if (c === '\u0000') {
36873                     break;
36874                 }
36875                 str += c;
36876             }
36877             return str;
36878         }
36879         return values;
36880     }
36881     
36882 });
36883
36884 Roo.apply(Roo.bootstrap.UploadCropbox, {
36885     tags : {
36886         'Orientation': 0x0112
36887     },
36888     
36889     Orientation: {
36890             1: 0, //'top-left',
36891 //            2: 'top-right',
36892             3: 180, //'bottom-right',
36893 //            4: 'bottom-left',
36894 //            5: 'left-top',
36895             6: 90, //'right-top',
36896 //            7: 'right-bottom',
36897             8: 270 //'left-bottom'
36898     },
36899     
36900     exifTagTypes : {
36901         // byte, 8-bit unsigned int:
36902         1: {
36903             getValue: function (dataView, dataOffset) {
36904                 return dataView.getUint8(dataOffset);
36905             },
36906             size: 1
36907         },
36908         // ascii, 8-bit byte:
36909         2: {
36910             getValue: function (dataView, dataOffset) {
36911                 return String.fromCharCode(dataView.getUint8(dataOffset));
36912             },
36913             size: 1,
36914             ascii: true
36915         },
36916         // short, 16 bit int:
36917         3: {
36918             getValue: function (dataView, dataOffset, littleEndian) {
36919                 return dataView.getUint16(dataOffset, littleEndian);
36920             },
36921             size: 2
36922         },
36923         // long, 32 bit int:
36924         4: {
36925             getValue: function (dataView, dataOffset, littleEndian) {
36926                 return dataView.getUint32(dataOffset, littleEndian);
36927             },
36928             size: 4
36929         },
36930         // rational = two long values, first is numerator, second is denominator:
36931         5: {
36932             getValue: function (dataView, dataOffset, littleEndian) {
36933                 return dataView.getUint32(dataOffset, littleEndian) /
36934                     dataView.getUint32(dataOffset + 4, littleEndian);
36935             },
36936             size: 8
36937         },
36938         // slong, 32 bit signed int:
36939         9: {
36940             getValue: function (dataView, dataOffset, littleEndian) {
36941                 return dataView.getInt32(dataOffset, littleEndian);
36942             },
36943             size: 4
36944         },
36945         // srational, two slongs, first is numerator, second is denominator:
36946         10: {
36947             getValue: function (dataView, dataOffset, littleEndian) {
36948                 return dataView.getInt32(dataOffset, littleEndian) /
36949                     dataView.getInt32(dataOffset + 4, littleEndian);
36950             },
36951             size: 8
36952         }
36953     },
36954     
36955     footer : {
36956         STANDARD : [
36957             {
36958                 tag : 'div',
36959                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36960                 action : 'rotate-left',
36961                 cn : [
36962                     {
36963                         tag : 'button',
36964                         cls : 'btn btn-default',
36965                         html : '<i class="fa fa-undo"></i>'
36966                     }
36967                 ]
36968             },
36969             {
36970                 tag : 'div',
36971                 cls : 'btn-group roo-upload-cropbox-picture',
36972                 action : 'picture',
36973                 cn : [
36974                     {
36975                         tag : 'button',
36976                         cls : 'btn btn-default',
36977                         html : '<i class="fa fa-picture-o"></i>'
36978                     }
36979                 ]
36980             },
36981             {
36982                 tag : 'div',
36983                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36984                 action : 'rotate-right',
36985                 cn : [
36986                     {
36987                         tag : 'button',
36988                         cls : 'btn btn-default',
36989                         html : '<i class="fa fa-repeat"></i>'
36990                     }
36991                 ]
36992             }
36993         ],
36994         DOCUMENT : [
36995             {
36996                 tag : 'div',
36997                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36998                 action : 'rotate-left',
36999                 cn : [
37000                     {
37001                         tag : 'button',
37002                         cls : 'btn btn-default',
37003                         html : '<i class="fa fa-undo"></i>'
37004                     }
37005                 ]
37006             },
37007             {
37008                 tag : 'div',
37009                 cls : 'btn-group roo-upload-cropbox-download',
37010                 action : 'download',
37011                 cn : [
37012                     {
37013                         tag : 'button',
37014                         cls : 'btn btn-default',
37015                         html : '<i class="fa fa-download"></i>'
37016                     }
37017                 ]
37018             },
37019             {
37020                 tag : 'div',
37021                 cls : 'btn-group roo-upload-cropbox-crop',
37022                 action : 'crop',
37023                 cn : [
37024                     {
37025                         tag : 'button',
37026                         cls : 'btn btn-default',
37027                         html : '<i class="fa fa-crop"></i>'
37028                     }
37029                 ]
37030             },
37031             {
37032                 tag : 'div',
37033                 cls : 'btn-group roo-upload-cropbox-trash',
37034                 action : 'trash',
37035                 cn : [
37036                     {
37037                         tag : 'button',
37038                         cls : 'btn btn-default',
37039                         html : '<i class="fa fa-trash"></i>'
37040                     }
37041                 ]
37042             },
37043             {
37044                 tag : 'div',
37045                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37046                 action : 'rotate-right',
37047                 cn : [
37048                     {
37049                         tag : 'button',
37050                         cls : 'btn btn-default',
37051                         html : '<i class="fa fa-repeat"></i>'
37052                     }
37053                 ]
37054             }
37055         ],
37056         ROTATOR : [
37057             {
37058                 tag : 'div',
37059                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37060                 action : 'rotate-left',
37061                 cn : [
37062                     {
37063                         tag : 'button',
37064                         cls : 'btn btn-default',
37065                         html : '<i class="fa fa-undo"></i>'
37066                     }
37067                 ]
37068             },
37069             {
37070                 tag : 'div',
37071                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37072                 action : 'rotate-right',
37073                 cn : [
37074                     {
37075                         tag : 'button',
37076                         cls : 'btn btn-default',
37077                         html : '<i class="fa fa-repeat"></i>'
37078                     }
37079                 ]
37080             }
37081         ]
37082     }
37083 });
37084
37085 /*
37086 * Licence: LGPL
37087 */
37088
37089 /**
37090  * @class Roo.bootstrap.DocumentManager
37091  * @extends Roo.bootstrap.Component
37092  * Bootstrap DocumentManager class
37093  * @cfg {String} paramName default 'imageUpload'
37094  * @cfg {String} toolTipName default 'filename'
37095  * @cfg {String} method default POST
37096  * @cfg {String} url action url
37097  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37098  * @cfg {Boolean} multiple multiple upload default true
37099  * @cfg {Number} thumbSize default 300
37100  * @cfg {String} fieldLabel
37101  * @cfg {Number} labelWidth default 4
37102  * @cfg {String} labelAlign (left|top) default left
37103  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37104 * @cfg {Number} labellg set the width of label (1-12)
37105  * @cfg {Number} labelmd set the width of label (1-12)
37106  * @cfg {Number} labelsm set the width of label (1-12)
37107  * @cfg {Number} labelxs set the width of label (1-12)
37108  * 
37109  * @constructor
37110  * Create a new DocumentManager
37111  * @param {Object} config The config object
37112  */
37113
37114 Roo.bootstrap.DocumentManager = function(config){
37115     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37116     
37117     this.files = [];
37118     this.delegates = [];
37119     
37120     this.addEvents({
37121         /**
37122          * @event initial
37123          * Fire when initial the DocumentManager
37124          * @param {Roo.bootstrap.DocumentManager} this
37125          */
37126         "initial" : true,
37127         /**
37128          * @event inspect
37129          * inspect selected file
37130          * @param {Roo.bootstrap.DocumentManager} this
37131          * @param {File} file
37132          */
37133         "inspect" : true,
37134         /**
37135          * @event exception
37136          * Fire when xhr load exception
37137          * @param {Roo.bootstrap.DocumentManager} this
37138          * @param {XMLHttpRequest} xhr
37139          */
37140         "exception" : true,
37141         /**
37142          * @event afterupload
37143          * Fire when xhr load exception
37144          * @param {Roo.bootstrap.DocumentManager} this
37145          * @param {XMLHttpRequest} xhr
37146          */
37147         "afterupload" : true,
37148         /**
37149          * @event prepare
37150          * prepare the form data
37151          * @param {Roo.bootstrap.DocumentManager} this
37152          * @param {Object} formData
37153          */
37154         "prepare" : true,
37155         /**
37156          * @event remove
37157          * Fire when remove the file
37158          * @param {Roo.bootstrap.DocumentManager} this
37159          * @param {Object} file
37160          */
37161         "remove" : true,
37162         /**
37163          * @event refresh
37164          * Fire after refresh the file
37165          * @param {Roo.bootstrap.DocumentManager} this
37166          */
37167         "refresh" : true,
37168         /**
37169          * @event click
37170          * Fire after click the image
37171          * @param {Roo.bootstrap.DocumentManager} this
37172          * @param {Object} file
37173          */
37174         "click" : true,
37175         /**
37176          * @event edit
37177          * Fire when upload a image and editable set to true
37178          * @param {Roo.bootstrap.DocumentManager} this
37179          * @param {Object} file
37180          */
37181         "edit" : true,
37182         /**
37183          * @event beforeselectfile
37184          * Fire before select file
37185          * @param {Roo.bootstrap.DocumentManager} this
37186          */
37187         "beforeselectfile" : true,
37188         /**
37189          * @event process
37190          * Fire before process file
37191          * @param {Roo.bootstrap.DocumentManager} this
37192          * @param {Object} file
37193          */
37194         "process" : true,
37195         /**
37196          * @event previewrendered
37197          * Fire when preview rendered
37198          * @param {Roo.bootstrap.DocumentManager} this
37199          * @param {Object} file
37200          */
37201         "previewrendered" : true,
37202         /**
37203          */
37204         "previewResize" : true
37205         
37206     });
37207 };
37208
37209 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
37210     
37211     boxes : 0,
37212     inputName : '',
37213     thumbSize : 300,
37214     multiple : true,
37215     files : false,
37216     method : 'POST',
37217     url : '',
37218     paramName : 'imageUpload',
37219     toolTipName : 'filename',
37220     fieldLabel : '',
37221     labelWidth : 4,
37222     labelAlign : 'left',
37223     editable : true,
37224     delegates : false,
37225     xhr : false, 
37226     
37227     labellg : 0,
37228     labelmd : 0,
37229     labelsm : 0,
37230     labelxs : 0,
37231     
37232     getAutoCreate : function()
37233     {   
37234         var managerWidget = {
37235             tag : 'div',
37236             cls : 'roo-document-manager',
37237             cn : [
37238                 {
37239                     tag : 'input',
37240                     cls : 'roo-document-manager-selector',
37241                     type : 'file'
37242                 },
37243                 {
37244                     tag : 'div',
37245                     cls : 'roo-document-manager-uploader',
37246                     cn : [
37247                         {
37248                             tag : 'div',
37249                             cls : 'roo-document-manager-upload-btn',
37250                             html : '<i class="fa fa-plus"></i>'
37251                         }
37252                     ]
37253                     
37254                 }
37255             ]
37256         };
37257         
37258         var content = [
37259             {
37260                 tag : 'div',
37261                 cls : 'column col-md-12',
37262                 cn : managerWidget
37263             }
37264         ];
37265         
37266         if(this.fieldLabel.length){
37267             
37268             content = [
37269                 {
37270                     tag : 'div',
37271                     cls : 'column col-md-12',
37272                     html : this.fieldLabel
37273                 },
37274                 {
37275                     tag : 'div',
37276                     cls : 'column col-md-12',
37277                     cn : managerWidget
37278                 }
37279             ];
37280
37281             if(this.labelAlign == 'left'){
37282                 content = [
37283                     {
37284                         tag : 'div',
37285                         cls : 'column',
37286                         html : this.fieldLabel
37287                     },
37288                     {
37289                         tag : 'div',
37290                         cls : 'column',
37291                         cn : managerWidget
37292                     }
37293                 ];
37294                 
37295                 if(this.labelWidth > 12){
37296                     content[0].style = "width: " + this.labelWidth + 'px';
37297                 }
37298
37299                 if(this.labelWidth < 13 && this.labelmd == 0){
37300                     this.labelmd = this.labelWidth;
37301                 }
37302
37303                 if(this.labellg > 0){
37304                     content[0].cls += ' col-lg-' + this.labellg;
37305                     content[1].cls += ' col-lg-' + (12 - this.labellg);
37306                 }
37307
37308                 if(this.labelmd > 0){
37309                     content[0].cls += ' col-md-' + this.labelmd;
37310                     content[1].cls += ' col-md-' + (12 - this.labelmd);
37311                 }
37312
37313                 if(this.labelsm > 0){
37314                     content[0].cls += ' col-sm-' + this.labelsm;
37315                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
37316                 }
37317
37318                 if(this.labelxs > 0){
37319                     content[0].cls += ' col-xs-' + this.labelxs;
37320                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
37321                 }
37322                 
37323             }
37324         }
37325         
37326         var cfg = {
37327             tag : 'div',
37328             cls : 'row clearfix',
37329             cn : content
37330         };
37331         
37332         return cfg;
37333         
37334     },
37335     
37336     initEvents : function()
37337     {
37338         this.managerEl = this.el.select('.roo-document-manager', true).first();
37339         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37340         
37341         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37342         this.selectorEl.hide();
37343         
37344         if(this.multiple){
37345             this.selectorEl.attr('multiple', 'multiple');
37346         }
37347         
37348         this.selectorEl.on('change', this.onFileSelected, this);
37349         
37350         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37351         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37352         
37353         this.uploader.on('click', this.onUploaderClick, this);
37354         
37355         this.renderProgressDialog();
37356         
37357         var _this = this;
37358         
37359         window.addEventListener("resize", function() { _this.refresh(); } );
37360         
37361         this.fireEvent('initial', this);
37362     },
37363     
37364     renderProgressDialog : function()
37365     {
37366         var _this = this;
37367         
37368         this.progressDialog = new Roo.bootstrap.Modal({
37369             cls : 'roo-document-manager-progress-dialog',
37370             allow_close : false,
37371             animate : false,
37372             title : '',
37373             buttons : [
37374                 {
37375                     name  :'cancel',
37376                     weight : 'danger',
37377                     html : 'Cancel'
37378                 }
37379             ], 
37380             listeners : { 
37381                 btnclick : function() {
37382                     _this.uploadCancel();
37383                     this.hide();
37384                 }
37385             }
37386         });
37387          
37388         this.progressDialog.render(Roo.get(document.body));
37389          
37390         this.progress = new Roo.bootstrap.Progress({
37391             cls : 'roo-document-manager-progress',
37392             active : true,
37393             striped : true
37394         });
37395         
37396         this.progress.render(this.progressDialog.getChildContainer());
37397         
37398         this.progressBar = new Roo.bootstrap.ProgressBar({
37399             cls : 'roo-document-manager-progress-bar',
37400             aria_valuenow : 0,
37401             aria_valuemin : 0,
37402             aria_valuemax : 12,
37403             panel : 'success'
37404         });
37405         
37406         this.progressBar.render(this.progress.getChildContainer());
37407     },
37408     
37409     onUploaderClick : function(e)
37410     {
37411         e.preventDefault();
37412      
37413         if(this.fireEvent('beforeselectfile', this) != false){
37414             this.selectorEl.dom.click();
37415         }
37416         
37417     },
37418     
37419     onFileSelected : function(e)
37420     {
37421         e.preventDefault();
37422         
37423         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37424             return;
37425         }
37426         
37427         Roo.each(this.selectorEl.dom.files, function(file){
37428             if(this.fireEvent('inspect', this, file) != false){
37429                 this.files.push(file);
37430             }
37431         }, this);
37432         
37433         this.queue();
37434         
37435     },
37436     
37437     queue : function()
37438     {
37439         this.selectorEl.dom.value = '';
37440         
37441         if(!this.files || !this.files.length){
37442             return;
37443         }
37444         
37445         if(this.boxes > 0 && this.files.length > this.boxes){
37446             this.files = this.files.slice(0, this.boxes);
37447         }
37448         
37449         this.uploader.show();
37450         
37451         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37452             this.uploader.hide();
37453         }
37454         
37455         var _this = this;
37456         
37457         var files = [];
37458         
37459         var docs = [];
37460         
37461         Roo.each(this.files, function(file){
37462             
37463             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37464                 var f = this.renderPreview(file);
37465                 files.push(f);
37466                 return;
37467             }
37468             
37469             if(file.type.indexOf('image') != -1){
37470                 this.delegates.push(
37471                     (function(){
37472                         _this.process(file);
37473                     }).createDelegate(this)
37474                 );
37475         
37476                 return;
37477             }
37478             
37479             docs.push(
37480                 (function(){
37481                     _this.process(file);
37482                 }).createDelegate(this)
37483             );
37484             
37485         }, this);
37486         
37487         this.files = files;
37488         
37489         this.delegates = this.delegates.concat(docs);
37490         
37491         if(!this.delegates.length){
37492             this.refresh();
37493             return;
37494         }
37495         
37496         this.progressBar.aria_valuemax = this.delegates.length;
37497         
37498         this.arrange();
37499         
37500         return;
37501     },
37502     
37503     arrange : function()
37504     {
37505         if(!this.delegates.length){
37506             this.progressDialog.hide();
37507             this.refresh();
37508             return;
37509         }
37510         
37511         var delegate = this.delegates.shift();
37512         
37513         this.progressDialog.show();
37514         
37515         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37516         
37517         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37518         
37519         delegate();
37520     },
37521     
37522     refresh : function()
37523     {
37524         this.uploader.show();
37525         
37526         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37527             this.uploader.hide();
37528         }
37529         
37530         Roo.isTouch ? this.closable(false) : this.closable(true);
37531         
37532         this.fireEvent('refresh', this);
37533     },
37534     
37535     onRemove : function(e, el, o)
37536     {
37537         e.preventDefault();
37538         
37539         this.fireEvent('remove', this, o);
37540         
37541     },
37542     
37543     remove : function(o)
37544     {
37545         var files = [];
37546         
37547         Roo.each(this.files, function(file){
37548             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37549                 files.push(file);
37550                 return;
37551             }
37552
37553             o.target.remove();
37554
37555         }, this);
37556         
37557         this.files = files;
37558         
37559         this.refresh();
37560     },
37561     
37562     clear : function()
37563     {
37564         Roo.each(this.files, function(file){
37565             if(!file.target){
37566                 return;
37567             }
37568             
37569             file.target.remove();
37570
37571         }, this);
37572         
37573         this.files = [];
37574         
37575         this.refresh();
37576     },
37577     
37578     onClick : function(e, el, o)
37579     {
37580         e.preventDefault();
37581         
37582         this.fireEvent('click', this, o);
37583         
37584     },
37585     
37586     closable : function(closable)
37587     {
37588         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37589             
37590             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37591             
37592             if(closable){
37593                 el.show();
37594                 return;
37595             }
37596             
37597             el.hide();
37598             
37599         }, this);
37600     },
37601     
37602     xhrOnLoad : function(xhr)
37603     {
37604         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37605             el.remove();
37606         }, this);
37607         
37608         if (xhr.readyState !== 4) {
37609             this.arrange();
37610             this.fireEvent('exception', this, xhr);
37611             return;
37612         }
37613
37614         var response = Roo.decode(xhr.responseText);
37615         
37616         if(!response.success){
37617             this.arrange();
37618             this.fireEvent('exception', this, xhr);
37619             return;
37620         }
37621         
37622         var file = this.renderPreview(response.data);
37623         
37624         this.files.push(file);
37625         
37626         this.arrange();
37627         
37628         this.fireEvent('afterupload', this, xhr);
37629         
37630     },
37631     
37632     xhrOnError : function(xhr)
37633     {
37634         Roo.log('xhr on error');
37635         
37636         var response = Roo.decode(xhr.responseText);
37637           
37638         Roo.log(response);
37639         
37640         this.arrange();
37641     },
37642     
37643     process : function(file)
37644     {
37645         if(this.fireEvent('process', this, file) !== false){
37646             if(this.editable && file.type.indexOf('image') != -1){
37647                 this.fireEvent('edit', this, file);
37648                 return;
37649             }
37650
37651             this.uploadStart(file, false);
37652
37653             return;
37654         }
37655         
37656     },
37657     
37658     uploadStart : function(file, crop)
37659     {
37660         this.xhr = new XMLHttpRequest();
37661         
37662         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37663             this.arrange();
37664             return;
37665         }
37666         
37667         file.xhr = this.xhr;
37668             
37669         this.managerEl.createChild({
37670             tag : 'div',
37671             cls : 'roo-document-manager-loading',
37672             cn : [
37673                 {
37674                     tag : 'div',
37675                     tooltip : file.name,
37676                     cls : 'roo-document-manager-thumb',
37677                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37678                 }
37679             ]
37680
37681         });
37682
37683         this.xhr.open(this.method, this.url, true);
37684         
37685         var headers = {
37686             "Accept": "application/json",
37687             "Cache-Control": "no-cache",
37688             "X-Requested-With": "XMLHttpRequest"
37689         };
37690         
37691         for (var headerName in headers) {
37692             var headerValue = headers[headerName];
37693             if (headerValue) {
37694                 this.xhr.setRequestHeader(headerName, headerValue);
37695             }
37696         }
37697         
37698         var _this = this;
37699         
37700         this.xhr.onload = function()
37701         {
37702             _this.xhrOnLoad(_this.xhr);
37703         }
37704         
37705         this.xhr.onerror = function()
37706         {
37707             _this.xhrOnError(_this.xhr);
37708         }
37709         
37710         var formData = new FormData();
37711
37712         formData.append('returnHTML', 'NO');
37713         
37714         if(crop){
37715             formData.append('crop', crop);
37716         }
37717         
37718         formData.append(this.paramName, file, file.name);
37719         
37720         var options = {
37721             file : file, 
37722             manually : false
37723         };
37724         
37725         if(this.fireEvent('prepare', this, formData, options) != false){
37726             
37727             if(options.manually){
37728                 return;
37729             }
37730             
37731             this.xhr.send(formData);
37732             return;
37733         };
37734         
37735         this.uploadCancel();
37736     },
37737     
37738     uploadCancel : function()
37739     {
37740         if (this.xhr) {
37741             this.xhr.abort();
37742         }
37743         
37744         this.delegates = [];
37745         
37746         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37747             el.remove();
37748         }, this);
37749         
37750         this.arrange();
37751     },
37752     
37753     renderPreview : function(file)
37754     {
37755         if(typeof(file.target) != 'undefined' && file.target){
37756             return file;
37757         }
37758         
37759         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37760         
37761         var previewEl = this.managerEl.createChild({
37762             tag : 'div',
37763             cls : 'roo-document-manager-preview',
37764             cn : [
37765                 {
37766                     tag : 'div',
37767                     tooltip : file[this.toolTipName],
37768                     cls : 'roo-document-manager-thumb',
37769                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37770                 },
37771                 {
37772                     tag : 'button',
37773                     cls : 'close',
37774                     html : '<i class="fa fa-times-circle"></i>'
37775                 }
37776             ]
37777         });
37778
37779         var close = previewEl.select('button.close', true).first();
37780
37781         close.on('click', this.onRemove, this, file);
37782
37783         file.target = previewEl;
37784
37785         var image = previewEl.select('img', true).first();
37786         
37787         var _this = this;
37788         
37789         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37790         
37791         image.on('click', this.onClick, this, file);
37792         
37793         this.fireEvent('previewrendered', this, file);
37794         
37795         return file;
37796         
37797     },
37798     
37799     onPreviewLoad : function(file, image)
37800     {
37801         if(typeof(file.target) == 'undefined' || !file.target){
37802             return;
37803         }
37804         
37805         var width = image.dom.naturalWidth || image.dom.width;
37806         var height = image.dom.naturalHeight || image.dom.height;
37807         
37808         if(!this.previewResize) {
37809             return;
37810         }
37811         
37812         if(width > height){
37813             file.target.addClass('wide');
37814             return;
37815         }
37816         
37817         file.target.addClass('tall');
37818         return;
37819         
37820     },
37821     
37822     uploadFromSource : function(file, crop)
37823     {
37824         this.xhr = new XMLHttpRequest();
37825         
37826         this.managerEl.createChild({
37827             tag : 'div',
37828             cls : 'roo-document-manager-loading',
37829             cn : [
37830                 {
37831                     tag : 'div',
37832                     tooltip : file.name,
37833                     cls : 'roo-document-manager-thumb',
37834                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37835                 }
37836             ]
37837
37838         });
37839
37840         this.xhr.open(this.method, this.url, true);
37841         
37842         var headers = {
37843             "Accept": "application/json",
37844             "Cache-Control": "no-cache",
37845             "X-Requested-With": "XMLHttpRequest"
37846         };
37847         
37848         for (var headerName in headers) {
37849             var headerValue = headers[headerName];
37850             if (headerValue) {
37851                 this.xhr.setRequestHeader(headerName, headerValue);
37852             }
37853         }
37854         
37855         var _this = this;
37856         
37857         this.xhr.onload = function()
37858         {
37859             _this.xhrOnLoad(_this.xhr);
37860         }
37861         
37862         this.xhr.onerror = function()
37863         {
37864             _this.xhrOnError(_this.xhr);
37865         }
37866         
37867         var formData = new FormData();
37868
37869         formData.append('returnHTML', 'NO');
37870         
37871         formData.append('crop', crop);
37872         
37873         if(typeof(file.filename) != 'undefined'){
37874             formData.append('filename', file.filename);
37875         }
37876         
37877         if(typeof(file.mimetype) != 'undefined'){
37878             formData.append('mimetype', file.mimetype);
37879         }
37880         
37881         Roo.log(formData);
37882         
37883         if(this.fireEvent('prepare', this, formData) != false){
37884             this.xhr.send(formData);
37885         };
37886     }
37887 });
37888
37889 /*
37890 * Licence: LGPL
37891 */
37892
37893 /**
37894  * @class Roo.bootstrap.DocumentViewer
37895  * @extends Roo.bootstrap.Component
37896  * Bootstrap DocumentViewer class
37897  * @cfg {Boolean} showDownload (true|false) show download button (default true)
37898  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37899  * 
37900  * @constructor
37901  * Create a new DocumentViewer
37902  * @param {Object} config The config object
37903  */
37904
37905 Roo.bootstrap.DocumentViewer = function(config){
37906     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37907     
37908     this.addEvents({
37909         /**
37910          * @event initial
37911          * Fire after initEvent
37912          * @param {Roo.bootstrap.DocumentViewer} this
37913          */
37914         "initial" : true,
37915         /**
37916          * @event click
37917          * Fire after click
37918          * @param {Roo.bootstrap.DocumentViewer} this
37919          */
37920         "click" : true,
37921         /**
37922          * @event download
37923          * Fire after download button
37924          * @param {Roo.bootstrap.DocumentViewer} this
37925          */
37926         "download" : true,
37927         /**
37928          * @event trash
37929          * Fire after trash button
37930          * @param {Roo.bootstrap.DocumentViewer} this
37931          */
37932         "trash" : true
37933         
37934     });
37935 };
37936
37937 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
37938     
37939     showDownload : true,
37940     
37941     showTrash : true,
37942     
37943     getAutoCreate : function()
37944     {
37945         var cfg = {
37946             tag : 'div',
37947             cls : 'roo-document-viewer',
37948             cn : [
37949                 {
37950                     tag : 'div',
37951                     cls : 'roo-document-viewer-body',
37952                     cn : [
37953                         {
37954                             tag : 'div',
37955                             cls : 'roo-document-viewer-thumb',
37956                             cn : [
37957                                 {
37958                                     tag : 'img',
37959                                     cls : 'roo-document-viewer-image'
37960                                 }
37961                             ]
37962                         }
37963                     ]
37964                 },
37965                 {
37966                     tag : 'div',
37967                     cls : 'roo-document-viewer-footer',
37968                     cn : {
37969                         tag : 'div',
37970                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37971                         cn : [
37972                             {
37973                                 tag : 'div',
37974                                 cls : 'btn-group roo-document-viewer-download',
37975                                 cn : [
37976                                     {
37977                                         tag : 'button',
37978                                         cls : 'btn btn-default',
37979                                         html : '<i class="fa fa-download"></i>'
37980                                     }
37981                                 ]
37982                             },
37983                             {
37984                                 tag : 'div',
37985                                 cls : 'btn-group roo-document-viewer-trash',
37986                                 cn : [
37987                                     {
37988                                         tag : 'button',
37989                                         cls : 'btn btn-default',
37990                                         html : '<i class="fa fa-trash"></i>'
37991                                     }
37992                                 ]
37993                             }
37994                         ]
37995                     }
37996                 }
37997             ]
37998         };
37999         
38000         return cfg;
38001     },
38002     
38003     initEvents : function()
38004     {
38005         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
38006         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
38007         
38008         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
38009         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
38010         
38011         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
38012         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
38013         
38014         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
38015         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
38016         
38017         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
38018         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
38019         
38020         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
38021         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38022         
38023         this.bodyEl.on('click', this.onClick, this);
38024         this.downloadBtn.on('click', this.onDownload, this);
38025         this.trashBtn.on('click', this.onTrash, this);
38026         
38027         this.downloadBtn.hide();
38028         this.trashBtn.hide();
38029         
38030         if(this.showDownload){
38031             this.downloadBtn.show();
38032         }
38033         
38034         if(this.showTrash){
38035             this.trashBtn.show();
38036         }
38037         
38038         if(!this.showDownload && !this.showTrash) {
38039             this.footerEl.hide();
38040         }
38041         
38042     },
38043     
38044     initial : function()
38045     {
38046         this.fireEvent('initial', this);
38047         
38048     },
38049     
38050     onClick : function(e)
38051     {
38052         e.preventDefault();
38053         
38054         this.fireEvent('click', this);
38055     },
38056     
38057     onDownload : function(e)
38058     {
38059         e.preventDefault();
38060         
38061         this.fireEvent('download', this);
38062     },
38063     
38064     onTrash : function(e)
38065     {
38066         e.preventDefault();
38067         
38068         this.fireEvent('trash', this);
38069     }
38070     
38071 });
38072 /*
38073  * - LGPL
38074  *
38075  * FieldLabel
38076  * 
38077  */
38078
38079 /**
38080  * @class Roo.bootstrap.form.FieldLabel
38081  * @extends Roo.bootstrap.Component
38082  * Bootstrap FieldLabel class
38083  * @cfg {String} html contents of the element
38084  * @cfg {String} tag tag of the element default label
38085  * @cfg {String} cls class of the element
38086  * @cfg {String} target label target 
38087  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38088  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38089  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38090  * @cfg {String} iconTooltip default "This field is required"
38091  * @cfg {String} indicatorpos (left|right) default left
38092  * 
38093  * @constructor
38094  * Create a new FieldLabel
38095  * @param {Object} config The config object
38096  */
38097
38098 Roo.bootstrap.form.FieldLabel = function(config){
38099     Roo.bootstrap.Element.superclass.constructor.call(this, config);
38100     
38101     this.addEvents({
38102             /**
38103              * @event invalid
38104              * Fires after the field has been marked as invalid.
38105              * @param {Roo.form.FieldLabel} this
38106              * @param {String} msg The validation message
38107              */
38108             invalid : true,
38109             /**
38110              * @event valid
38111              * Fires after the field has been validated with no errors.
38112              * @param {Roo.form.FieldLabel} this
38113              */
38114             valid : true
38115         });
38116 };
38117
38118 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
38119     
38120     tag: 'label',
38121     cls: '',
38122     html: '',
38123     target: '',
38124     allowBlank : true,
38125     invalidClass : 'has-warning',
38126     validClass : 'has-success',
38127     iconTooltip : 'This field is required',
38128     indicatorpos : 'left',
38129     
38130     getAutoCreate : function(){
38131         
38132         var cls = "";
38133         if (!this.allowBlank) {
38134             cls  = "visible";
38135         }
38136         
38137         var cfg = {
38138             tag : this.tag,
38139             cls : 'roo-bootstrap-field-label ' + this.cls,
38140             for : this.target,
38141             cn : [
38142                 {
38143                     tag : 'i',
38144                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38145                     tooltip : this.iconTooltip
38146                 },
38147                 {
38148                     tag : 'span',
38149                     html : this.html
38150                 }
38151             ] 
38152         };
38153         
38154         if(this.indicatorpos == 'right'){
38155             var cfg = {
38156                 tag : this.tag,
38157                 cls : 'roo-bootstrap-field-label ' + this.cls,
38158                 for : this.target,
38159                 cn : [
38160                     {
38161                         tag : 'span',
38162                         html : this.html
38163                     },
38164                     {
38165                         tag : 'i',
38166                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38167                         tooltip : this.iconTooltip
38168                     }
38169                 ] 
38170             };
38171         }
38172         
38173         return cfg;
38174     },
38175     
38176     initEvents: function() 
38177     {
38178         Roo.bootstrap.Element.superclass.initEvents.call(this);
38179         
38180         this.indicator = this.indicatorEl();
38181         
38182         if(this.indicator){
38183             this.indicator.removeClass('visible');
38184             this.indicator.addClass('invisible');
38185         }
38186         
38187         Roo.bootstrap.form.FieldLabel.register(this);
38188     },
38189     
38190     indicatorEl : function()
38191     {
38192         var indicator = this.el.select('i.roo-required-indicator',true).first();
38193         
38194         if(!indicator){
38195             return false;
38196         }
38197         
38198         return indicator;
38199         
38200     },
38201     
38202     /**
38203      * Mark this field as valid
38204      */
38205     markValid : function()
38206     {
38207         if(this.indicator){
38208             this.indicator.removeClass('visible');
38209             this.indicator.addClass('invisible');
38210         }
38211         if (Roo.bootstrap.version == 3) {
38212             this.el.removeClass(this.invalidClass);
38213             this.el.addClass(this.validClass);
38214         } else {
38215             this.el.removeClass('is-invalid');
38216             this.el.addClass('is-valid');
38217         }
38218         
38219         
38220         this.fireEvent('valid', this);
38221     },
38222     
38223     /**
38224      * Mark this field as invalid
38225      * @param {String} msg The validation message
38226      */
38227     markInvalid : function(msg)
38228     {
38229         if(this.indicator){
38230             this.indicator.removeClass('invisible');
38231             this.indicator.addClass('visible');
38232         }
38233           if (Roo.bootstrap.version == 3) {
38234             this.el.removeClass(this.validClass);
38235             this.el.addClass(this.invalidClass);
38236         } else {
38237             this.el.removeClass('is-valid');
38238             this.el.addClass('is-invalid');
38239         }
38240         
38241         
38242         this.fireEvent('invalid', this, msg);
38243     }
38244     
38245    
38246 });
38247
38248 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38249     
38250     groups: {},
38251     
38252      /**
38253     * register a FieldLabel Group
38254     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38255     */
38256     register : function(label)
38257     {
38258         if(this.groups.hasOwnProperty(label.target)){
38259             return;
38260         }
38261      
38262         this.groups[label.target] = label;
38263         
38264     },
38265     /**
38266     * fetch a FieldLabel Group based on the target
38267     * @param {string} target
38268     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38269     */
38270     get: function(target) {
38271         if (typeof(this.groups[target]) == 'undefined') {
38272             return false;
38273         }
38274         
38275         return this.groups[target] ;
38276     }
38277 });
38278
38279  
38280
38281  /*
38282  * - LGPL
38283  *
38284  * page DateSplitField.
38285  * 
38286  */
38287
38288
38289 /**
38290  * @class Roo.bootstrap.form.DateSplitField
38291  * @extends Roo.bootstrap.Component
38292  * Bootstrap DateSplitField class
38293  * @cfg {string} fieldLabel - the label associated
38294  * @cfg {Number} labelWidth set the width of label (0-12)
38295  * @cfg {String} labelAlign (top|left)
38296  * @cfg {Boolean} dayAllowBlank (true|false) default false
38297  * @cfg {Boolean} monthAllowBlank (true|false) default false
38298  * @cfg {Boolean} yearAllowBlank (true|false) default false
38299  * @cfg {string} dayPlaceholder 
38300  * @cfg {string} monthPlaceholder
38301  * @cfg {string} yearPlaceholder
38302  * @cfg {string} dayFormat default 'd'
38303  * @cfg {string} monthFormat default 'm'
38304  * @cfg {string} yearFormat default 'Y'
38305  * @cfg {Number} labellg set the width of label (1-12)
38306  * @cfg {Number} labelmd set the width of label (1-12)
38307  * @cfg {Number} labelsm set the width of label (1-12)
38308  * @cfg {Number} labelxs set the width of label (1-12)
38309
38310  *     
38311  * @constructor
38312  * Create a new DateSplitField
38313  * @param {Object} config The config object
38314  */
38315
38316 Roo.bootstrap.form.DateSplitField = function(config){
38317     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38318     
38319     this.addEvents({
38320         // raw events
38321          /**
38322          * @event years
38323          * getting the data of years
38324          * @param {Roo.bootstrap.form.DateSplitField} this
38325          * @param {Object} years
38326          */
38327         "years" : true,
38328         /**
38329          * @event days
38330          * getting the data of days
38331          * @param {Roo.bootstrap.form.DateSplitField} this
38332          * @param {Object} days
38333          */
38334         "days" : true,
38335         /**
38336          * @event invalid
38337          * Fires after the field has been marked as invalid.
38338          * @param {Roo.form.Field} this
38339          * @param {String} msg The validation message
38340          */
38341         invalid : true,
38342        /**
38343          * @event valid
38344          * Fires after the field has been validated with no errors.
38345          * @param {Roo.form.Field} this
38346          */
38347         valid : true
38348     });
38349 };
38350
38351 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
38352     
38353     fieldLabel : '',
38354     labelAlign : 'top',
38355     labelWidth : 3,
38356     dayAllowBlank : false,
38357     monthAllowBlank : false,
38358     yearAllowBlank : false,
38359     dayPlaceholder : '',
38360     monthPlaceholder : '',
38361     yearPlaceholder : '',
38362     dayFormat : 'd',
38363     monthFormat : 'm',
38364     yearFormat : 'Y',
38365     isFormField : true,
38366     labellg : 0,
38367     labelmd : 0,
38368     labelsm : 0,
38369     labelxs : 0,
38370     
38371     getAutoCreate : function()
38372     {
38373         var cfg = {
38374             tag : 'div',
38375             cls : 'row roo-date-split-field-group',
38376             cn : [
38377                 {
38378                     tag : 'input',
38379                     type : 'hidden',
38380                     cls : 'form-hidden-field roo-date-split-field-group-value',
38381                     name : this.name
38382                 }
38383             ]
38384         };
38385         
38386         var labelCls = 'col-md-12';
38387         var contentCls = 'col-md-4';
38388         
38389         if(this.fieldLabel){
38390             
38391             var label = {
38392                 tag : 'div',
38393                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38394                 cn : [
38395                     {
38396                         tag : 'label',
38397                         html : this.fieldLabel
38398                     }
38399                 ]
38400             };
38401             
38402             if(this.labelAlign == 'left'){
38403             
38404                 if(this.labelWidth > 12){
38405                     label.style = "width: " + this.labelWidth + 'px';
38406                 }
38407
38408                 if(this.labelWidth < 13 && this.labelmd == 0){
38409                     this.labelmd = this.labelWidth;
38410                 }
38411
38412                 if(this.labellg > 0){
38413                     labelCls = ' col-lg-' + this.labellg;
38414                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38415                 }
38416
38417                 if(this.labelmd > 0){
38418                     labelCls = ' col-md-' + this.labelmd;
38419                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38420                 }
38421
38422                 if(this.labelsm > 0){
38423                     labelCls = ' col-sm-' + this.labelsm;
38424                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38425                 }
38426
38427                 if(this.labelxs > 0){
38428                     labelCls = ' col-xs-' + this.labelxs;
38429                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38430                 }
38431             }
38432             
38433             label.cls += ' ' + labelCls;
38434             
38435             cfg.cn.push(label);
38436         }
38437         
38438         Roo.each(['day', 'month', 'year'], function(t){
38439             cfg.cn.push({
38440                 tag : 'div',
38441                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38442             });
38443         }, this);
38444         
38445         return cfg;
38446     },
38447     
38448     inputEl: function ()
38449     {
38450         return this.el.select('.roo-date-split-field-group-value', true).first();
38451     },
38452     
38453     onRender : function(ct, position) 
38454     {
38455         var _this = this;
38456         
38457         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38458         
38459         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38460         
38461         this.dayField = new Roo.bootstrap.form.ComboBox({
38462             allowBlank : this.dayAllowBlank,
38463             alwaysQuery : true,
38464             displayField : 'value',
38465             editable : false,
38466             fieldLabel : '',
38467             forceSelection : true,
38468             mode : 'local',
38469             placeholder : this.dayPlaceholder,
38470             selectOnFocus : true,
38471             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38472             triggerAction : 'all',
38473             typeAhead : true,
38474             valueField : 'value',
38475             store : new Roo.data.SimpleStore({
38476                 data : (function() {    
38477                     var days = [];
38478                     _this.fireEvent('days', _this, days);
38479                     return days;
38480                 })(),
38481                 fields : [ 'value' ]
38482             }),
38483             listeners : {
38484                 select : function (_self, record, index)
38485                 {
38486                     _this.setValue(_this.getValue());
38487                 }
38488             }
38489         });
38490
38491         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38492         
38493         this.monthField = new Roo.bootstrap.form.MonthField({
38494             after : '<i class=\"fa fa-calendar\"></i>',
38495             allowBlank : this.monthAllowBlank,
38496             placeholder : this.monthPlaceholder,
38497             readOnly : true,
38498             listeners : {
38499                 render : function (_self)
38500                 {
38501                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
38502                         e.preventDefault();
38503                         _self.focus();
38504                     });
38505                 },
38506                 select : function (_self, oldvalue, newvalue)
38507                 {
38508                     _this.setValue(_this.getValue());
38509                 }
38510             }
38511         });
38512         
38513         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38514         
38515         this.yearField = new Roo.bootstrap.form.ComboBox({
38516             allowBlank : this.yearAllowBlank,
38517             alwaysQuery : true,
38518             displayField : 'value',
38519             editable : false,
38520             fieldLabel : '',
38521             forceSelection : true,
38522             mode : 'local',
38523             placeholder : this.yearPlaceholder,
38524             selectOnFocus : true,
38525             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38526             triggerAction : 'all',
38527             typeAhead : true,
38528             valueField : 'value',
38529             store : new Roo.data.SimpleStore({
38530                 data : (function() {
38531                     var years = [];
38532                     _this.fireEvent('years', _this, years);
38533                     return years;
38534                 })(),
38535                 fields : [ 'value' ]
38536             }),
38537             listeners : {
38538                 select : function (_self, record, index)
38539                 {
38540                     _this.setValue(_this.getValue());
38541                 }
38542             }
38543         });
38544
38545         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38546     },
38547     
38548     setValue : function(v, format)
38549     {
38550         this.inputEl.dom.value = v;
38551         
38552         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38553         
38554         var d = Date.parseDate(v, f);
38555         
38556         if(!d){
38557             this.validate();
38558             return;
38559         }
38560         
38561         this.setDay(d.format(this.dayFormat));
38562         this.setMonth(d.format(this.monthFormat));
38563         this.setYear(d.format(this.yearFormat));
38564         
38565         this.validate();
38566         
38567         return;
38568     },
38569     
38570     setDay : function(v)
38571     {
38572         this.dayField.setValue(v);
38573         this.inputEl.dom.value = this.getValue();
38574         this.validate();
38575         return;
38576     },
38577     
38578     setMonth : function(v)
38579     {
38580         this.monthField.setValue(v, true);
38581         this.inputEl.dom.value = this.getValue();
38582         this.validate();
38583         return;
38584     },
38585     
38586     setYear : function(v)
38587     {
38588         this.yearField.setValue(v);
38589         this.inputEl.dom.value = this.getValue();
38590         this.validate();
38591         return;
38592     },
38593     
38594     getDay : function()
38595     {
38596         return this.dayField.getValue();
38597     },
38598     
38599     getMonth : function()
38600     {
38601         return this.monthField.getValue();
38602     },
38603     
38604     getYear : function()
38605     {
38606         return this.yearField.getValue();
38607     },
38608     
38609     getValue : function()
38610     {
38611         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38612         
38613         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38614         
38615         return date;
38616     },
38617     
38618     reset : function()
38619     {
38620         this.setDay('');
38621         this.setMonth('');
38622         this.setYear('');
38623         this.inputEl.dom.value = '';
38624         this.validate();
38625         return;
38626     },
38627     
38628     validate : function()
38629     {
38630         var d = this.dayField.validate();
38631         var m = this.monthField.validate();
38632         var y = this.yearField.validate();
38633         
38634         var valid = true;
38635         
38636         if(
38637                 (!this.dayAllowBlank && !d) ||
38638                 (!this.monthAllowBlank && !m) ||
38639                 (!this.yearAllowBlank && !y)
38640         ){
38641             valid = false;
38642         }
38643         
38644         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38645             return valid;
38646         }
38647         
38648         if(valid){
38649             this.markValid();
38650             return valid;
38651         }
38652         
38653         this.markInvalid();
38654         
38655         return valid;
38656     },
38657     
38658     markValid : function()
38659     {
38660         
38661         var label = this.el.select('label', true).first();
38662         var icon = this.el.select('i.fa-star', true).first();
38663
38664         if(label && icon){
38665             icon.remove();
38666         }
38667         
38668         this.fireEvent('valid', this);
38669     },
38670     
38671      /**
38672      * Mark this field as invalid
38673      * @param {String} msg The validation message
38674      */
38675     markInvalid : function(msg)
38676     {
38677         
38678         var label = this.el.select('label', true).first();
38679         var icon = this.el.select('i.fa-star', true).first();
38680
38681         if(label && !icon){
38682             this.el.select('.roo-date-split-field-label', true).createChild({
38683                 tag : 'i',
38684                 cls : 'text-danger fa fa-lg fa-star',
38685                 tooltip : 'This field is required',
38686                 style : 'margin-right:5px;'
38687             }, label, true);
38688         }
38689         
38690         this.fireEvent('invalid', this, msg);
38691     },
38692     
38693     clearInvalid : function()
38694     {
38695         var label = this.el.select('label', true).first();
38696         var icon = this.el.select('i.fa-star', true).first();
38697
38698         if(label && icon){
38699             icon.remove();
38700         }
38701         
38702         this.fireEvent('valid', this);
38703     },
38704     
38705     getName: function()
38706     {
38707         return this.name;
38708     }
38709     
38710 });
38711
38712  
38713
38714 /**
38715  * @class Roo.bootstrap.LayoutMasonry
38716  * @extends Roo.bootstrap.Component
38717  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38718  * Bootstrap Layout Masonry class
38719  *
38720  * This is based on 
38721  * http://masonry.desandro.com
38722  *
38723  * The idea is to render all the bricks based on vertical width...
38724  *
38725  * The original code extends 'outlayer' - we might need to use that....
38726
38727  * @constructor
38728  * Create a new Element
38729  * @param {Object} config The config object
38730  */
38731
38732 Roo.bootstrap.LayoutMasonry = function(config){
38733     
38734     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38735     
38736     this.bricks = [];
38737     
38738     Roo.bootstrap.LayoutMasonry.register(this);
38739     
38740     this.addEvents({
38741         // raw events
38742         /**
38743          * @event layout
38744          * Fire after layout the items
38745          * @param {Roo.bootstrap.LayoutMasonry} this
38746          * @param {Roo.EventObject} e
38747          */
38748         "layout" : true
38749     });
38750     
38751 };
38752
38753 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
38754     
38755     /**
38756      * @cfg {Boolean} isLayoutInstant = no animation?
38757      */   
38758     isLayoutInstant : false, // needed?
38759    
38760     /**
38761      * @cfg {Number} boxWidth  width of the columns
38762      */   
38763     boxWidth : 450,
38764     
38765       /**
38766      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
38767      */   
38768     boxHeight : 0,
38769     
38770     /**
38771      * @cfg {Number} padWidth padding below box..
38772      */   
38773     padWidth : 10, 
38774     
38775     /**
38776      * @cfg {Number} gutter gutter width..
38777      */   
38778     gutter : 10,
38779     
38780      /**
38781      * @cfg {Number} maxCols maximum number of columns
38782      */   
38783     
38784     maxCols: 0,
38785     
38786     /**
38787      * @cfg {Boolean} isAutoInitial defalut true
38788      */   
38789     isAutoInitial : true, 
38790     
38791     containerWidth: 0,
38792     
38793     /**
38794      * @cfg {Boolean} isHorizontal defalut false
38795      */   
38796     isHorizontal : false, 
38797
38798     currentSize : null,
38799     
38800     tag: 'div',
38801     
38802     cls: '',
38803     
38804     bricks: null, //CompositeElement
38805     
38806     cols : 1,
38807     
38808     _isLayoutInited : false,
38809     
38810 //    isAlternative : false, // only use for vertical layout...
38811     
38812     /**
38813      * @cfg {Number} alternativePadWidth padding below box..
38814      */   
38815     alternativePadWidth : 50,
38816     
38817     selectedBrick : [],
38818     
38819     getAutoCreate : function(){
38820         
38821         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38822         
38823         var cfg = {
38824             tag: this.tag,
38825             cls: 'blog-masonary-wrapper ' + this.cls,
38826             cn : {
38827                 cls : 'mas-boxes masonary'
38828             }
38829         };
38830         
38831         return cfg;
38832     },
38833     
38834     getChildContainer: function( )
38835     {
38836         if (this.boxesEl) {
38837             return this.boxesEl;
38838         }
38839         
38840         this.boxesEl = this.el.select('.mas-boxes').first();
38841         
38842         return this.boxesEl;
38843     },
38844     
38845     
38846     initEvents : function()
38847     {
38848         var _this = this;
38849         
38850         if(this.isAutoInitial){
38851             Roo.log('hook children rendered');
38852             this.on('childrenrendered', function() {
38853                 Roo.log('children rendered');
38854                 _this.initial();
38855             } ,this);
38856         }
38857     },
38858     
38859     initial : function()
38860     {
38861         this.selectedBrick = [];
38862         
38863         this.currentSize = this.el.getBox(true);
38864         
38865         Roo.EventManager.onWindowResize(this.resize, this); 
38866
38867         if(!this.isAutoInitial){
38868             this.layout();
38869             return;
38870         }
38871         
38872         this.layout();
38873         
38874         return;
38875         //this.layout.defer(500,this);
38876         
38877     },
38878     
38879     resize : function()
38880     {
38881         var cs = this.el.getBox(true);
38882         
38883         if (
38884                 this.currentSize.width == cs.width && 
38885                 this.currentSize.x == cs.x && 
38886                 this.currentSize.height == cs.height && 
38887                 this.currentSize.y == cs.y 
38888         ) {
38889             Roo.log("no change in with or X or Y");
38890             return;
38891         }
38892         
38893         this.currentSize = cs;
38894         
38895         this.layout();
38896         
38897     },
38898     
38899     layout : function()
38900     {   
38901         this._resetLayout();
38902         
38903         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38904         
38905         this.layoutItems( isInstant );
38906       
38907         this._isLayoutInited = true;
38908         
38909         this.fireEvent('layout', this);
38910         
38911     },
38912     
38913     _resetLayout : function()
38914     {
38915         if(this.isHorizontal){
38916             this.horizontalMeasureColumns();
38917             return;
38918         }
38919         
38920         this.verticalMeasureColumns();
38921         
38922     },
38923     
38924     verticalMeasureColumns : function()
38925     {
38926         this.getContainerWidth();
38927         
38928 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38929 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
38930 //            return;
38931 //        }
38932         
38933         var boxWidth = this.boxWidth + this.padWidth;
38934         
38935         if(this.containerWidth < this.boxWidth){
38936             boxWidth = this.containerWidth
38937         }
38938         
38939         var containerWidth = this.containerWidth;
38940         
38941         var cols = Math.floor(containerWidth / boxWidth);
38942         
38943         this.cols = Math.max( cols, 1 );
38944         
38945         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38946         
38947         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38948         
38949         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38950         
38951         this.colWidth = boxWidth + avail - this.padWidth;
38952         
38953         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38954         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
38955     },
38956     
38957     horizontalMeasureColumns : function()
38958     {
38959         this.getContainerWidth();
38960         
38961         var boxWidth = this.boxWidth;
38962         
38963         if(this.containerWidth < boxWidth){
38964             boxWidth = this.containerWidth;
38965         }
38966         
38967         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38968         
38969         this.el.setHeight(boxWidth);
38970         
38971     },
38972     
38973     getContainerWidth : function()
38974     {
38975         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38976     },
38977     
38978     layoutItems : function( isInstant )
38979     {
38980         Roo.log(this.bricks);
38981         
38982         var items = Roo.apply([], this.bricks);
38983         
38984         if(this.isHorizontal){
38985             this._horizontalLayoutItems( items , isInstant );
38986             return;
38987         }
38988         
38989 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38990 //            this._verticalAlternativeLayoutItems( items , isInstant );
38991 //            return;
38992 //        }
38993         
38994         this._verticalLayoutItems( items , isInstant );
38995         
38996     },
38997     
38998     _verticalLayoutItems : function ( items , isInstant)
38999     {
39000         if ( !items || !items.length ) {
39001             return;
39002         }
39003         
39004         var standard = [
39005             ['xs', 'xs', 'xs', 'tall'],
39006             ['xs', 'xs', 'tall'],
39007             ['xs', 'xs', 'sm'],
39008             ['xs', 'xs', 'xs'],
39009             ['xs', 'tall'],
39010             ['xs', 'sm'],
39011             ['xs', 'xs'],
39012             ['xs'],
39013             
39014             ['sm', 'xs', 'xs'],
39015             ['sm', 'xs'],
39016             ['sm'],
39017             
39018             ['tall', 'xs', 'xs', 'xs'],
39019             ['tall', 'xs', 'xs'],
39020             ['tall', 'xs'],
39021             ['tall']
39022             
39023         ];
39024         
39025         var queue = [];
39026         
39027         var boxes = [];
39028         
39029         var box = [];
39030         
39031         Roo.each(items, function(item, k){
39032             
39033             switch (item.size) {
39034                 // these layouts take up a full box,
39035                 case 'md' :
39036                 case 'md-left' :
39037                 case 'md-right' :
39038                 case 'wide' :
39039                     
39040                     if(box.length){
39041                         boxes.push(box);
39042                         box = [];
39043                     }
39044                     
39045                     boxes.push([item]);
39046                     
39047                     break;
39048                     
39049                 case 'xs' :
39050                 case 'sm' :
39051                 case 'tall' :
39052                     
39053                     box.push(item);
39054                     
39055                     break;
39056                 default :
39057                     break;
39058                     
39059             }
39060             
39061         }, this);
39062         
39063         if(box.length){
39064             boxes.push(box);
39065             box = [];
39066         }
39067         
39068         var filterPattern = function(box, length)
39069         {
39070             if(!box.length){
39071                 return;
39072             }
39073             
39074             var match = false;
39075             
39076             var pattern = box.slice(0, length);
39077             
39078             var format = [];
39079             
39080             Roo.each(pattern, function(i){
39081                 format.push(i.size);
39082             }, this);
39083             
39084             Roo.each(standard, function(s){
39085                 
39086                 if(String(s) != String(format)){
39087                     return;
39088                 }
39089                 
39090                 match = true;
39091                 return false;
39092                 
39093             }, this);
39094             
39095             if(!match && length == 1){
39096                 return;
39097             }
39098             
39099             if(!match){
39100                 filterPattern(box, length - 1);
39101                 return;
39102             }
39103                 
39104             queue.push(pattern);
39105
39106             box = box.slice(length, box.length);
39107
39108             filterPattern(box, 4);
39109
39110             return;
39111             
39112         }
39113         
39114         Roo.each(boxes, function(box, k){
39115             
39116             if(!box.length){
39117                 return;
39118             }
39119             
39120             if(box.length == 1){
39121                 queue.push(box);
39122                 return;
39123             }
39124             
39125             filterPattern(box, 4);
39126             
39127         }, this);
39128         
39129         this._processVerticalLayoutQueue( queue, isInstant );
39130         
39131     },
39132     
39133 //    _verticalAlternativeLayoutItems : function( items , isInstant )
39134 //    {
39135 //        if ( !items || !items.length ) {
39136 //            return;
39137 //        }
39138 //
39139 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
39140 //        
39141 //    },
39142     
39143     _horizontalLayoutItems : function ( items , isInstant)
39144     {
39145         if ( !items || !items.length || items.length < 3) {
39146             return;
39147         }
39148         
39149         items.reverse();
39150         
39151         var eItems = items.slice(0, 3);
39152         
39153         items = items.slice(3, items.length);
39154         
39155         var standard = [
39156             ['xs', 'xs', 'xs', 'wide'],
39157             ['xs', 'xs', 'wide'],
39158             ['xs', 'xs', 'sm'],
39159             ['xs', 'xs', 'xs'],
39160             ['xs', 'wide'],
39161             ['xs', 'sm'],
39162             ['xs', 'xs'],
39163             ['xs'],
39164             
39165             ['sm', 'xs', 'xs'],
39166             ['sm', 'xs'],
39167             ['sm'],
39168             
39169             ['wide', 'xs', 'xs', 'xs'],
39170             ['wide', 'xs', 'xs'],
39171             ['wide', 'xs'],
39172             ['wide'],
39173             
39174             ['wide-thin']
39175         ];
39176         
39177         var queue = [];
39178         
39179         var boxes = [];
39180         
39181         var box = [];
39182         
39183         Roo.each(items, function(item, k){
39184             
39185             switch (item.size) {
39186                 case 'md' :
39187                 case 'md-left' :
39188                 case 'md-right' :
39189                 case 'tall' :
39190                     
39191                     if(box.length){
39192                         boxes.push(box);
39193                         box = [];
39194                     }
39195                     
39196                     boxes.push([item]);
39197                     
39198                     break;
39199                     
39200                 case 'xs' :
39201                 case 'sm' :
39202                 case 'wide' :
39203                 case 'wide-thin' :
39204                     
39205                     box.push(item);
39206                     
39207                     break;
39208                 default :
39209                     break;
39210                     
39211             }
39212             
39213         }, this);
39214         
39215         if(box.length){
39216             boxes.push(box);
39217             box = [];
39218         }
39219         
39220         var filterPattern = function(box, length)
39221         {
39222             if(!box.length){
39223                 return;
39224             }
39225             
39226             var match = false;
39227             
39228             var pattern = box.slice(0, length);
39229             
39230             var format = [];
39231             
39232             Roo.each(pattern, function(i){
39233                 format.push(i.size);
39234             }, this);
39235             
39236             Roo.each(standard, function(s){
39237                 
39238                 if(String(s) != String(format)){
39239                     return;
39240                 }
39241                 
39242                 match = true;
39243                 return false;
39244                 
39245             }, this);
39246             
39247             if(!match && length == 1){
39248                 return;
39249             }
39250             
39251             if(!match){
39252                 filterPattern(box, length - 1);
39253                 return;
39254             }
39255                 
39256             queue.push(pattern);
39257
39258             box = box.slice(length, box.length);
39259
39260             filterPattern(box, 4);
39261
39262             return;
39263             
39264         }
39265         
39266         Roo.each(boxes, function(box, k){
39267             
39268             if(!box.length){
39269                 return;
39270             }
39271             
39272             if(box.length == 1){
39273                 queue.push(box);
39274                 return;
39275             }
39276             
39277             filterPattern(box, 4);
39278             
39279         }, this);
39280         
39281         
39282         var prune = [];
39283         
39284         var pos = this.el.getBox(true);
39285         
39286         var minX = pos.x;
39287         
39288         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39289         
39290         var hit_end = false;
39291         
39292         Roo.each(queue, function(box){
39293             
39294             if(hit_end){
39295                 
39296                 Roo.each(box, function(b){
39297                 
39298                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39299                     b.el.hide();
39300
39301                 }, this);
39302
39303                 return;
39304             }
39305             
39306             var mx = 0;
39307             
39308             Roo.each(box, function(b){
39309                 
39310                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39311                 b.el.show();
39312
39313                 mx = Math.max(mx, b.x);
39314                 
39315             }, this);
39316             
39317             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39318             
39319             if(maxX < minX){
39320                 
39321                 Roo.each(box, function(b){
39322                 
39323                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39324                     b.el.hide();
39325                     
39326                 }, this);
39327                 
39328                 hit_end = true;
39329                 
39330                 return;
39331             }
39332             
39333             prune.push(box);
39334             
39335         }, this);
39336         
39337         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39338     },
39339     
39340     /** Sets position of item in DOM
39341     * @param {Element} item
39342     * @param {Number} x - horizontal position
39343     * @param {Number} y - vertical position
39344     * @param {Boolean} isInstant - disables transitions
39345     */
39346     _processVerticalLayoutQueue : function( queue, isInstant )
39347     {
39348         var pos = this.el.getBox(true);
39349         var x = pos.x;
39350         var y = pos.y;
39351         var maxY = [];
39352         
39353         for (var i = 0; i < this.cols; i++){
39354             maxY[i] = pos.y;
39355         }
39356         
39357         Roo.each(queue, function(box, k){
39358             
39359             var col = k % this.cols;
39360             
39361             Roo.each(box, function(b,kk){
39362                 
39363                 b.el.position('absolute');
39364                 
39365                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39366                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39367                 
39368                 if(b.size == 'md-left' || b.size == 'md-right'){
39369                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39370                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39371                 }
39372                 
39373                 b.el.setWidth(width);
39374                 b.el.setHeight(height);
39375                 // iframe?
39376                 b.el.select('iframe',true).setSize(width,height);
39377                 
39378             }, this);
39379             
39380             for (var i = 0; i < this.cols; i++){
39381                 
39382                 if(maxY[i] < maxY[col]){
39383                     col = i;
39384                     continue;
39385                 }
39386                 
39387                 col = Math.min(col, i);
39388                 
39389             }
39390             
39391             x = pos.x + col * (this.colWidth + this.padWidth);
39392             
39393             y = maxY[col];
39394             
39395             var positions = [];
39396             
39397             switch (box.length){
39398                 case 1 :
39399                     positions = this.getVerticalOneBoxColPositions(x, y, box);
39400                     break;
39401                 case 2 :
39402                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
39403                     break;
39404                 case 3 :
39405                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
39406                     break;
39407                 case 4 :
39408                     positions = this.getVerticalFourBoxColPositions(x, y, box);
39409                     break;
39410                 default :
39411                     break;
39412             }
39413             
39414             Roo.each(box, function(b,kk){
39415                 
39416                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39417                 
39418                 var sz = b.el.getSize();
39419                 
39420                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39421                 
39422             }, this);
39423             
39424         }, this);
39425         
39426         var mY = 0;
39427         
39428         for (var i = 0; i < this.cols; i++){
39429             mY = Math.max(mY, maxY[i]);
39430         }
39431         
39432         this.el.setHeight(mY - pos.y);
39433         
39434     },
39435     
39436 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39437 //    {
39438 //        var pos = this.el.getBox(true);
39439 //        var x = pos.x;
39440 //        var y = pos.y;
39441 //        var maxX = pos.right;
39442 //        
39443 //        var maxHeight = 0;
39444 //        
39445 //        Roo.each(items, function(item, k){
39446 //            
39447 //            var c = k % 2;
39448 //            
39449 //            item.el.position('absolute');
39450 //                
39451 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39452 //
39453 //            item.el.setWidth(width);
39454 //
39455 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39456 //
39457 //            item.el.setHeight(height);
39458 //            
39459 //            if(c == 0){
39460 //                item.el.setXY([x, y], isInstant ? false : true);
39461 //            } else {
39462 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
39463 //            }
39464 //            
39465 //            y = y + height + this.alternativePadWidth;
39466 //            
39467 //            maxHeight = maxHeight + height + this.alternativePadWidth;
39468 //            
39469 //        }, this);
39470 //        
39471 //        this.el.setHeight(maxHeight);
39472 //        
39473 //    },
39474     
39475     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39476     {
39477         var pos = this.el.getBox(true);
39478         
39479         var minX = pos.x;
39480         var minY = pos.y;
39481         
39482         var maxX = pos.right;
39483         
39484         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39485         
39486         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39487         
39488         Roo.each(queue, function(box, k){
39489             
39490             Roo.each(box, function(b, kk){
39491                 
39492                 b.el.position('absolute');
39493                 
39494                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39495                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39496                 
39497                 if(b.size == 'md-left' || b.size == 'md-right'){
39498                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39499                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39500                 }
39501                 
39502                 b.el.setWidth(width);
39503                 b.el.setHeight(height);
39504                 
39505             }, this);
39506             
39507             if(!box.length){
39508                 return;
39509             }
39510             
39511             var positions = [];
39512             
39513             switch (box.length){
39514                 case 1 :
39515                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39516                     break;
39517                 case 2 :
39518                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39519                     break;
39520                 case 3 :
39521                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39522                     break;
39523                 case 4 :
39524                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39525                     break;
39526                 default :
39527                     break;
39528             }
39529             
39530             Roo.each(box, function(b,kk){
39531                 
39532                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39533                 
39534                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39535                 
39536             }, this);
39537             
39538         }, this);
39539         
39540     },
39541     
39542     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39543     {
39544         Roo.each(eItems, function(b,k){
39545             
39546             b.size = (k == 0) ? 'sm' : 'xs';
39547             b.x = (k == 0) ? 2 : 1;
39548             b.y = (k == 0) ? 2 : 1;
39549             
39550             b.el.position('absolute');
39551             
39552             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39553                 
39554             b.el.setWidth(width);
39555             
39556             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39557             
39558             b.el.setHeight(height);
39559             
39560         }, this);
39561
39562         var positions = [];
39563         
39564         positions.push({
39565             x : maxX - this.unitWidth * 2 - this.gutter,
39566             y : minY
39567         });
39568         
39569         positions.push({
39570             x : maxX - this.unitWidth,
39571             y : minY + (this.unitWidth + this.gutter) * 2
39572         });
39573         
39574         positions.push({
39575             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39576             y : minY
39577         });
39578         
39579         Roo.each(eItems, function(b,k){
39580             
39581             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39582
39583         }, this);
39584         
39585     },
39586     
39587     getVerticalOneBoxColPositions : function(x, y, box)
39588     {
39589         var pos = [];
39590         
39591         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39592         
39593         if(box[0].size == 'md-left'){
39594             rand = 0;
39595         }
39596         
39597         if(box[0].size == 'md-right'){
39598             rand = 1;
39599         }
39600         
39601         pos.push({
39602             x : x + (this.unitWidth + this.gutter) * rand,
39603             y : y
39604         });
39605         
39606         return pos;
39607     },
39608     
39609     getVerticalTwoBoxColPositions : function(x, y, box)
39610     {
39611         var pos = [];
39612         
39613         if(box[0].size == 'xs'){
39614             
39615             pos.push({
39616                 x : x,
39617                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39618             });
39619
39620             pos.push({
39621                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39622                 y : y
39623             });
39624             
39625             return pos;
39626             
39627         }
39628         
39629         pos.push({
39630             x : x,
39631             y : y
39632         });
39633
39634         pos.push({
39635             x : x + (this.unitWidth + this.gutter) * 2,
39636             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39637         });
39638         
39639         return pos;
39640         
39641     },
39642     
39643     getVerticalThreeBoxColPositions : function(x, y, box)
39644     {
39645         var pos = [];
39646         
39647         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39648             
39649             pos.push({
39650                 x : x,
39651                 y : y
39652             });
39653
39654             pos.push({
39655                 x : x + (this.unitWidth + this.gutter) * 1,
39656                 y : y
39657             });
39658             
39659             pos.push({
39660                 x : x + (this.unitWidth + this.gutter) * 2,
39661                 y : y
39662             });
39663             
39664             return pos;
39665             
39666         }
39667         
39668         if(box[0].size == 'xs' && box[1].size == 'xs'){
39669             
39670             pos.push({
39671                 x : x,
39672                 y : y
39673             });
39674
39675             pos.push({
39676                 x : x,
39677                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39678             });
39679             
39680             pos.push({
39681                 x : x + (this.unitWidth + this.gutter) * 1,
39682                 y : y
39683             });
39684             
39685             return pos;
39686             
39687         }
39688         
39689         pos.push({
39690             x : x,
39691             y : y
39692         });
39693
39694         pos.push({
39695             x : x + (this.unitWidth + this.gutter) * 2,
39696             y : y
39697         });
39698
39699         pos.push({
39700             x : x + (this.unitWidth + this.gutter) * 2,
39701             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39702         });
39703             
39704         return pos;
39705         
39706     },
39707     
39708     getVerticalFourBoxColPositions : function(x, y, box)
39709     {
39710         var pos = [];
39711         
39712         if(box[0].size == 'xs'){
39713             
39714             pos.push({
39715                 x : x,
39716                 y : y
39717             });
39718
39719             pos.push({
39720                 x : x,
39721                 y : y + (this.unitHeight + this.gutter) * 1
39722             });
39723             
39724             pos.push({
39725                 x : x,
39726                 y : y + (this.unitHeight + this.gutter) * 2
39727             });
39728             
39729             pos.push({
39730                 x : x + (this.unitWidth + this.gutter) * 1,
39731                 y : y
39732             });
39733             
39734             return pos;
39735             
39736         }
39737         
39738         pos.push({
39739             x : x,
39740             y : y
39741         });
39742
39743         pos.push({
39744             x : x + (this.unitWidth + this.gutter) * 2,
39745             y : y
39746         });
39747
39748         pos.push({
39749             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39750             y : y + (this.unitHeight + this.gutter) * 1
39751         });
39752
39753         pos.push({
39754             x : x + (this.unitWidth + this.gutter) * 2,
39755             y : y + (this.unitWidth + this.gutter) * 2
39756         });
39757
39758         return pos;
39759         
39760     },
39761     
39762     getHorizontalOneBoxColPositions : function(maxX, minY, box)
39763     {
39764         var pos = [];
39765         
39766         if(box[0].size == 'md-left'){
39767             pos.push({
39768                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39769                 y : minY
39770             });
39771             
39772             return pos;
39773         }
39774         
39775         if(box[0].size == 'md-right'){
39776             pos.push({
39777                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39778                 y : minY + (this.unitWidth + this.gutter) * 1
39779             });
39780             
39781             return pos;
39782         }
39783         
39784         var rand = Math.floor(Math.random() * (4 - box[0].y));
39785         
39786         pos.push({
39787             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39788             y : minY + (this.unitWidth + this.gutter) * rand
39789         });
39790         
39791         return pos;
39792         
39793     },
39794     
39795     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39796     {
39797         var pos = [];
39798         
39799         if(box[0].size == 'xs'){
39800             
39801             pos.push({
39802                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39803                 y : minY
39804             });
39805
39806             pos.push({
39807                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39808                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39809             });
39810             
39811             return pos;
39812             
39813         }
39814         
39815         pos.push({
39816             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39817             y : minY
39818         });
39819
39820         pos.push({
39821             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39822             y : minY + (this.unitWidth + this.gutter) * 2
39823         });
39824         
39825         return pos;
39826         
39827     },
39828     
39829     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39830     {
39831         var pos = [];
39832         
39833         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39834             
39835             pos.push({
39836                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39837                 y : minY
39838             });
39839
39840             pos.push({
39841                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39842                 y : minY + (this.unitWidth + this.gutter) * 1
39843             });
39844             
39845             pos.push({
39846                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39847                 y : minY + (this.unitWidth + this.gutter) * 2
39848             });
39849             
39850             return pos;
39851             
39852         }
39853         
39854         if(box[0].size == 'xs' && box[1].size == 'xs'){
39855             
39856             pos.push({
39857                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39858                 y : minY
39859             });
39860
39861             pos.push({
39862                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39863                 y : minY
39864             });
39865             
39866             pos.push({
39867                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39868                 y : minY + (this.unitWidth + this.gutter) * 1
39869             });
39870             
39871             return pos;
39872             
39873         }
39874         
39875         pos.push({
39876             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39877             y : minY
39878         });
39879
39880         pos.push({
39881             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39882             y : minY + (this.unitWidth + this.gutter) * 2
39883         });
39884
39885         pos.push({
39886             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39887             y : minY + (this.unitWidth + this.gutter) * 2
39888         });
39889             
39890         return pos;
39891         
39892     },
39893     
39894     getHorizontalFourBoxColPositions : function(maxX, minY, box)
39895     {
39896         var pos = [];
39897         
39898         if(box[0].size == 'xs'){
39899             
39900             pos.push({
39901                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39902                 y : minY
39903             });
39904
39905             pos.push({
39906                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39907                 y : minY
39908             });
39909             
39910             pos.push({
39911                 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),
39912                 y : minY
39913             });
39914             
39915             pos.push({
39916                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39917                 y : minY + (this.unitWidth + this.gutter) * 1
39918             });
39919             
39920             return pos;
39921             
39922         }
39923         
39924         pos.push({
39925             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39926             y : minY
39927         });
39928         
39929         pos.push({
39930             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39931             y : minY + (this.unitWidth + this.gutter) * 2
39932         });
39933         
39934         pos.push({
39935             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39936             y : minY + (this.unitWidth + this.gutter) * 2
39937         });
39938         
39939         pos.push({
39940             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),
39941             y : minY + (this.unitWidth + this.gutter) * 2
39942         });
39943
39944         return pos;
39945         
39946     },
39947     
39948     /**
39949     * remove a Masonry Brick
39950     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39951     */
39952     removeBrick : function(brick_id)
39953     {
39954         if (!brick_id) {
39955             return;
39956         }
39957         
39958         for (var i = 0; i<this.bricks.length; i++) {
39959             if (this.bricks[i].id == brick_id) {
39960                 this.bricks.splice(i,1);
39961                 this.el.dom.removeChild(Roo.get(brick_id).dom);
39962                 this.initial();
39963             }
39964         }
39965     },
39966     
39967     /**
39968     * adds a Masonry Brick
39969     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39970     */
39971     addBrick : function(cfg)
39972     {
39973         var cn = new Roo.bootstrap.MasonryBrick(cfg);
39974         //this.register(cn);
39975         cn.parentId = this.id;
39976         cn.render(this.el);
39977         return cn;
39978     },
39979     
39980     /**
39981     * register a Masonry Brick
39982     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39983     */
39984     
39985     register : function(brick)
39986     {
39987         this.bricks.push(brick);
39988         brick.masonryId = this.id;
39989     },
39990     
39991     /**
39992     * clear all the Masonry Brick
39993     */
39994     clearAll : function()
39995     {
39996         this.bricks = [];
39997         //this.getChildContainer().dom.innerHTML = "";
39998         this.el.dom.innerHTML = '';
39999     },
40000     
40001     getSelected : function()
40002     {
40003         if (!this.selectedBrick) {
40004             return false;
40005         }
40006         
40007         return this.selectedBrick;
40008     }
40009 });
40010
40011 Roo.apply(Roo.bootstrap.LayoutMasonry, {
40012     
40013     groups: {},
40014      /**
40015     * register a Masonry Layout
40016     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
40017     */
40018     
40019     register : function(layout)
40020     {
40021         this.groups[layout.id] = layout;
40022     },
40023     /**
40024     * fetch a  Masonry Layout based on the masonry layout ID
40025     * @param {string} the masonry layout to add
40026     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40027     */
40028     
40029     get: function(layout_id) {
40030         if (typeof(this.groups[layout_id]) == 'undefined') {
40031             return false;
40032         }
40033         return this.groups[layout_id] ;
40034     }
40035     
40036     
40037     
40038 });
40039
40040  
40041
40042  /**
40043  *
40044  * This is based on 
40045  * http://masonry.desandro.com
40046  *
40047  * The idea is to render all the bricks based on vertical width...
40048  *
40049  * The original code extends 'outlayer' - we might need to use that....
40050  * 
40051  */
40052
40053
40054 /**
40055  * @class Roo.bootstrap.LayoutMasonryAuto
40056  * @extends Roo.bootstrap.Component
40057  * Bootstrap Layout Masonry class
40058  * 
40059  * @constructor
40060  * Create a new Element
40061  * @param {Object} config The config object
40062  */
40063
40064 Roo.bootstrap.LayoutMasonryAuto = function(config){
40065     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40066 };
40067
40068 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
40069     
40070       /**
40071      * @cfg {Boolean} isFitWidth  - resize the width..
40072      */   
40073     isFitWidth : false,  // options..
40074     /**
40075      * @cfg {Boolean} isOriginLeft = left align?
40076      */   
40077     isOriginLeft : true,
40078     /**
40079      * @cfg {Boolean} isOriginTop = top align?
40080      */   
40081     isOriginTop : false,
40082     /**
40083      * @cfg {Boolean} isLayoutInstant = no animation?
40084      */   
40085     isLayoutInstant : false, // needed?
40086     /**
40087      * @cfg {Boolean} isResizingContainer = not sure if this is used..
40088      */   
40089     isResizingContainer : true,
40090     /**
40091      * @cfg {Number} columnWidth  width of the columns 
40092      */   
40093     
40094     columnWidth : 0,
40095     
40096     /**
40097      * @cfg {Number} maxCols maximum number of columns
40098      */   
40099     
40100     maxCols: 0,
40101     /**
40102      * @cfg {Number} padHeight padding below box..
40103      */   
40104     
40105     padHeight : 10, 
40106     
40107     /**
40108      * @cfg {Boolean} isAutoInitial defalut true
40109      */   
40110     
40111     isAutoInitial : true, 
40112     
40113     // private?
40114     gutter : 0,
40115     
40116     containerWidth: 0,
40117     initialColumnWidth : 0,
40118     currentSize : null,
40119     
40120     colYs : null, // array.
40121     maxY : 0,
40122     padWidth: 10,
40123     
40124     
40125     tag: 'div',
40126     cls: '',
40127     bricks: null, //CompositeElement
40128     cols : 0, // array?
40129     // element : null, // wrapped now this.el
40130     _isLayoutInited : null, 
40131     
40132     
40133     getAutoCreate : function(){
40134         
40135         var cfg = {
40136             tag: this.tag,
40137             cls: 'blog-masonary-wrapper ' + this.cls,
40138             cn : {
40139                 cls : 'mas-boxes masonary'
40140             }
40141         };
40142         
40143         return cfg;
40144     },
40145     
40146     getChildContainer: function( )
40147     {
40148         if (this.boxesEl) {
40149             return this.boxesEl;
40150         }
40151         
40152         this.boxesEl = this.el.select('.mas-boxes').first();
40153         
40154         return this.boxesEl;
40155     },
40156     
40157     
40158     initEvents : function()
40159     {
40160         var _this = this;
40161         
40162         if(this.isAutoInitial){
40163             Roo.log('hook children rendered');
40164             this.on('childrenrendered', function() {
40165                 Roo.log('children rendered');
40166                 _this.initial();
40167             } ,this);
40168         }
40169         
40170     },
40171     
40172     initial : function()
40173     {
40174         this.reloadItems();
40175
40176         this.currentSize = this.el.getBox(true);
40177
40178         /// was window resize... - let's see if this works..
40179         Roo.EventManager.onWindowResize(this.resize, this); 
40180
40181         if(!this.isAutoInitial){
40182             this.layout();
40183             return;
40184         }
40185         
40186         this.layout.defer(500,this);
40187     },
40188     
40189     reloadItems: function()
40190     {
40191         this.bricks = this.el.select('.masonry-brick', true);
40192         
40193         this.bricks.each(function(b) {
40194             //Roo.log(b.getSize());
40195             if (!b.attr('originalwidth')) {
40196                 b.attr('originalwidth',  b.getSize().width);
40197             }
40198             
40199         });
40200         
40201         Roo.log(this.bricks.elements.length);
40202     },
40203     
40204     resize : function()
40205     {
40206         Roo.log('resize');
40207         var cs = this.el.getBox(true);
40208         
40209         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40210             Roo.log("no change in with or X");
40211             return;
40212         }
40213         this.currentSize = cs;
40214         this.layout();
40215     },
40216     
40217     layout : function()
40218     {
40219          Roo.log('layout');
40220         this._resetLayout();
40221         //this._manageStamps();
40222       
40223         // don't animate first layout
40224         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40225         this.layoutItems( isInstant );
40226       
40227         // flag for initalized
40228         this._isLayoutInited = true;
40229     },
40230     
40231     layoutItems : function( isInstant )
40232     {
40233         //var items = this._getItemsForLayout( this.items );
40234         // original code supports filtering layout items.. we just ignore it..
40235         
40236         this._layoutItems( this.bricks , isInstant );
40237       
40238         this._postLayout();
40239     },
40240     _layoutItems : function ( items , isInstant)
40241     {
40242        //this.fireEvent( 'layout', this, items );
40243     
40244
40245         if ( !items || !items.elements.length ) {
40246           // no items, emit event with empty array
40247             return;
40248         }
40249
40250         var queue = [];
40251         items.each(function(item) {
40252             Roo.log("layout item");
40253             Roo.log(item);
40254             // get x/y object from method
40255             var position = this._getItemLayoutPosition( item );
40256             // enqueue
40257             position.item = item;
40258             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40259             queue.push( position );
40260         }, this);
40261       
40262         this._processLayoutQueue( queue );
40263     },
40264     /** Sets position of item in DOM
40265     * @param {Element} item
40266     * @param {Number} x - horizontal position
40267     * @param {Number} y - vertical position
40268     * @param {Boolean} isInstant - disables transitions
40269     */
40270     _processLayoutQueue : function( queue )
40271     {
40272         for ( var i=0, len = queue.length; i < len; i++ ) {
40273             var obj = queue[i];
40274             obj.item.position('absolute');
40275             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40276         }
40277     },
40278       
40279     
40280     /**
40281     * Any logic you want to do after each layout,
40282     * i.e. size the container
40283     */
40284     _postLayout : function()
40285     {
40286         this.resizeContainer();
40287     },
40288     
40289     resizeContainer : function()
40290     {
40291         if ( !this.isResizingContainer ) {
40292             return;
40293         }
40294         var size = this._getContainerSize();
40295         if ( size ) {
40296             this.el.setSize(size.width,size.height);
40297             this.boxesEl.setSize(size.width,size.height);
40298         }
40299     },
40300     
40301     
40302     
40303     _resetLayout : function()
40304     {
40305         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40306         this.colWidth = this.el.getWidth();
40307         //this.gutter = this.el.getWidth(); 
40308         
40309         this.measureColumns();
40310
40311         // reset column Y
40312         var i = this.cols;
40313         this.colYs = [];
40314         while (i--) {
40315             this.colYs.push( 0 );
40316         }
40317     
40318         this.maxY = 0;
40319     },
40320
40321     measureColumns : function()
40322     {
40323         this.getContainerWidth();
40324       // if columnWidth is 0, default to outerWidth of first item
40325         if ( !this.columnWidth ) {
40326             var firstItem = this.bricks.first();
40327             Roo.log(firstItem);
40328             this.columnWidth  = this.containerWidth;
40329             if (firstItem && firstItem.attr('originalwidth') ) {
40330                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40331             }
40332             // columnWidth fall back to item of first element
40333             Roo.log("set column width?");
40334                         this.initialColumnWidth = this.columnWidth  ;
40335
40336             // if first elem has no width, default to size of container
40337             
40338         }
40339         
40340         
40341         if (this.initialColumnWidth) {
40342             this.columnWidth = this.initialColumnWidth;
40343         }
40344         
40345         
40346             
40347         // column width is fixed at the top - however if container width get's smaller we should
40348         // reduce it...
40349         
40350         // this bit calcs how man columns..
40351             
40352         var columnWidth = this.columnWidth += this.gutter;
40353       
40354         // calculate columns
40355         var containerWidth = this.containerWidth + this.gutter;
40356         
40357         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40358         // fix rounding errors, typically with gutters
40359         var excess = columnWidth - containerWidth % columnWidth;
40360         
40361         
40362         // if overshoot is less than a pixel, round up, otherwise floor it
40363         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40364         cols = Math[ mathMethod ]( cols );
40365         this.cols = Math.max( cols, 1 );
40366         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40367         
40368          // padding positioning..
40369         var totalColWidth = this.cols * this.columnWidth;
40370         var padavail = this.containerWidth - totalColWidth;
40371         // so for 2 columns - we need 3 'pads'
40372         
40373         var padNeeded = (1+this.cols) * this.padWidth;
40374         
40375         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40376         
40377         this.columnWidth += padExtra
40378         //this.padWidth = Math.floor(padavail /  ( this.cols));
40379         
40380         // adjust colum width so that padding is fixed??
40381         
40382         // we have 3 columns ... total = width * 3
40383         // we have X left over... that should be used by 
40384         
40385         //if (this.expandC) {
40386             
40387         //}
40388         
40389         
40390         
40391     },
40392     
40393     getContainerWidth : function()
40394     {
40395        /* // container is parent if fit width
40396         var container = this.isFitWidth ? this.element.parentNode : this.element;
40397         // check that this.size and size are there
40398         // IE8 triggers resize on body size change, so they might not be
40399         
40400         var size = getSize( container );  //FIXME
40401         this.containerWidth = size && size.innerWidth; //FIXME
40402         */
40403          
40404         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
40405         
40406     },
40407     
40408     _getItemLayoutPosition : function( item )  // what is item?
40409     {
40410         // we resize the item to our columnWidth..
40411       
40412         item.setWidth(this.columnWidth);
40413         item.autoBoxAdjust  = false;
40414         
40415         var sz = item.getSize();
40416  
40417         // how many columns does this brick span
40418         var remainder = this.containerWidth % this.columnWidth;
40419         
40420         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40421         // round if off by 1 pixel, otherwise use ceil
40422         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
40423         colSpan = Math.min( colSpan, this.cols );
40424         
40425         // normally this should be '1' as we dont' currently allow multi width columns..
40426         
40427         var colGroup = this._getColGroup( colSpan );
40428         // get the minimum Y value from the columns
40429         var minimumY = Math.min.apply( Math, colGroup );
40430         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40431         
40432         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
40433          
40434         // position the brick
40435         var position = {
40436             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40437             y: this.currentSize.y + minimumY + this.padHeight
40438         };
40439         
40440         Roo.log(position);
40441         // apply setHeight to necessary columns
40442         var setHeight = minimumY + sz.height + this.padHeight;
40443         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40444         
40445         var setSpan = this.cols + 1 - colGroup.length;
40446         for ( var i = 0; i < setSpan; i++ ) {
40447           this.colYs[ shortColIndex + i ] = setHeight ;
40448         }
40449       
40450         return position;
40451     },
40452     
40453     /**
40454      * @param {Number} colSpan - number of columns the element spans
40455      * @returns {Array} colGroup
40456      */
40457     _getColGroup : function( colSpan )
40458     {
40459         if ( colSpan < 2 ) {
40460           // if brick spans only one column, use all the column Ys
40461           return this.colYs;
40462         }
40463       
40464         var colGroup = [];
40465         // how many different places could this brick fit horizontally
40466         var groupCount = this.cols + 1 - colSpan;
40467         // for each group potential horizontal position
40468         for ( var i = 0; i < groupCount; i++ ) {
40469           // make an array of colY values for that one group
40470           var groupColYs = this.colYs.slice( i, i + colSpan );
40471           // and get the max value of the array
40472           colGroup[i] = Math.max.apply( Math, groupColYs );
40473         }
40474         return colGroup;
40475     },
40476     /*
40477     _manageStamp : function( stamp )
40478     {
40479         var stampSize =  stamp.getSize();
40480         var offset = stamp.getBox();
40481         // get the columns that this stamp affects
40482         var firstX = this.isOriginLeft ? offset.x : offset.right;
40483         var lastX = firstX + stampSize.width;
40484         var firstCol = Math.floor( firstX / this.columnWidth );
40485         firstCol = Math.max( 0, firstCol );
40486         
40487         var lastCol = Math.floor( lastX / this.columnWidth );
40488         // lastCol should not go over if multiple of columnWidth #425
40489         lastCol -= lastX % this.columnWidth ? 0 : 1;
40490         lastCol = Math.min( this.cols - 1, lastCol );
40491         
40492         // set colYs to bottom of the stamp
40493         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40494             stampSize.height;
40495             
40496         for ( var i = firstCol; i <= lastCol; i++ ) {
40497           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40498         }
40499     },
40500     */
40501     
40502     _getContainerSize : function()
40503     {
40504         this.maxY = Math.max.apply( Math, this.colYs );
40505         var size = {
40506             height: this.maxY
40507         };
40508       
40509         if ( this.isFitWidth ) {
40510             size.width = this._getContainerFitWidth();
40511         }
40512       
40513         return size;
40514     },
40515     
40516     _getContainerFitWidth : function()
40517     {
40518         var unusedCols = 0;
40519         // count unused columns
40520         var i = this.cols;
40521         while ( --i ) {
40522           if ( this.colYs[i] !== 0 ) {
40523             break;
40524           }
40525           unusedCols++;
40526         }
40527         // fit container to columns that have been used
40528         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40529     },
40530     
40531     needsResizeLayout : function()
40532     {
40533         var previousWidth = this.containerWidth;
40534         this.getContainerWidth();
40535         return previousWidth !== this.containerWidth;
40536     }
40537  
40538 });
40539
40540  
40541
40542  /*
40543  * - LGPL
40544  *
40545  * element
40546  * 
40547  */
40548
40549 /**
40550  * @class Roo.bootstrap.MasonryBrick
40551  * @extends Roo.bootstrap.Component
40552  * Bootstrap MasonryBrick class
40553  * 
40554  * @constructor
40555  * Create a new MasonryBrick
40556  * @param {Object} config The config object
40557  */
40558
40559 Roo.bootstrap.MasonryBrick = function(config){
40560     
40561     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40562     
40563     Roo.bootstrap.MasonryBrick.register(this);
40564     
40565     this.addEvents({
40566         // raw events
40567         /**
40568          * @event click
40569          * When a MasonryBrick is clcik
40570          * @param {Roo.bootstrap.MasonryBrick} this
40571          * @param {Roo.EventObject} e
40572          */
40573         "click" : true
40574     });
40575 };
40576
40577 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40578     
40579     /**
40580      * @cfg {String} title
40581      */   
40582     title : '',
40583     /**
40584      * @cfg {String} html
40585      */   
40586     html : '',
40587     /**
40588      * @cfg {String} bgimage
40589      */   
40590     bgimage : '',
40591     /**
40592      * @cfg {String} videourl
40593      */   
40594     videourl : '',
40595     /**
40596      * @cfg {String} cls
40597      */   
40598     cls : '',
40599     /**
40600      * @cfg {String} href
40601      */   
40602     href : '',
40603     /**
40604      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40605      */   
40606     size : 'xs',
40607     
40608     /**
40609      * @cfg {String} placetitle (center|bottom)
40610      */   
40611     placetitle : '',
40612     
40613     /**
40614      * @cfg {Boolean} isFitContainer defalut true
40615      */   
40616     isFitContainer : true, 
40617     
40618     /**
40619      * @cfg {Boolean} preventDefault defalut false
40620      */   
40621     preventDefault : false, 
40622     
40623     /**
40624      * @cfg {Boolean} inverse defalut false
40625      */   
40626     maskInverse : false, 
40627     
40628     getAutoCreate : function()
40629     {
40630         if(!this.isFitContainer){
40631             return this.getSplitAutoCreate();
40632         }
40633         
40634         var cls = 'masonry-brick masonry-brick-full';
40635         
40636         if(this.href.length){
40637             cls += ' masonry-brick-link';
40638         }
40639         
40640         if(this.bgimage.length){
40641             cls += ' masonry-brick-image';
40642         }
40643         
40644         if(this.maskInverse){
40645             cls += ' mask-inverse';
40646         }
40647         
40648         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40649             cls += ' enable-mask';
40650         }
40651         
40652         if(this.size){
40653             cls += ' masonry-' + this.size + '-brick';
40654         }
40655         
40656         if(this.placetitle.length){
40657             
40658             switch (this.placetitle) {
40659                 case 'center' :
40660                     cls += ' masonry-center-title';
40661                     break;
40662                 case 'bottom' :
40663                     cls += ' masonry-bottom-title';
40664                     break;
40665                 default:
40666                     break;
40667             }
40668             
40669         } else {
40670             if(!this.html.length && !this.bgimage.length){
40671                 cls += ' masonry-center-title';
40672             }
40673
40674             if(!this.html.length && this.bgimage.length){
40675                 cls += ' masonry-bottom-title';
40676             }
40677         }
40678         
40679         if(this.cls){
40680             cls += ' ' + this.cls;
40681         }
40682         
40683         var cfg = {
40684             tag: (this.href.length) ? 'a' : 'div',
40685             cls: cls,
40686             cn: [
40687                 {
40688                     tag: 'div',
40689                     cls: 'masonry-brick-mask'
40690                 },
40691                 {
40692                     tag: 'div',
40693                     cls: 'masonry-brick-paragraph',
40694                     cn: []
40695                 }
40696             ]
40697         };
40698         
40699         if(this.href.length){
40700             cfg.href = this.href;
40701         }
40702         
40703         var cn = cfg.cn[1].cn;
40704         
40705         if(this.title.length){
40706             cn.push({
40707                 tag: 'h4',
40708                 cls: 'masonry-brick-title',
40709                 html: this.title
40710             });
40711         }
40712         
40713         if(this.html.length){
40714             cn.push({
40715                 tag: 'p',
40716                 cls: 'masonry-brick-text',
40717                 html: this.html
40718             });
40719         }
40720         
40721         if (!this.title.length && !this.html.length) {
40722             cfg.cn[1].cls += ' hide';
40723         }
40724         
40725         if(this.bgimage.length){
40726             cfg.cn.push({
40727                 tag: 'img',
40728                 cls: 'masonry-brick-image-view',
40729                 src: this.bgimage
40730             });
40731         }
40732         
40733         if(this.videourl.length){
40734             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40735             // youtube support only?
40736             cfg.cn.push({
40737                 tag: 'iframe',
40738                 cls: 'masonry-brick-image-view',
40739                 src: vurl,
40740                 frameborder : 0,
40741                 allowfullscreen : true
40742             });
40743         }
40744         
40745         return cfg;
40746         
40747     },
40748     
40749     getSplitAutoCreate : function()
40750     {
40751         var cls = 'masonry-brick masonry-brick-split';
40752         
40753         if(this.href.length){
40754             cls += ' masonry-brick-link';
40755         }
40756         
40757         if(this.bgimage.length){
40758             cls += ' masonry-brick-image';
40759         }
40760         
40761         if(this.size){
40762             cls += ' masonry-' + this.size + '-brick';
40763         }
40764         
40765         switch (this.placetitle) {
40766             case 'center' :
40767                 cls += ' masonry-center-title';
40768                 break;
40769             case 'bottom' :
40770                 cls += ' masonry-bottom-title';
40771                 break;
40772             default:
40773                 if(!this.bgimage.length){
40774                     cls += ' masonry-center-title';
40775                 }
40776
40777                 if(this.bgimage.length){
40778                     cls += ' masonry-bottom-title';
40779                 }
40780                 break;
40781         }
40782         
40783         if(this.cls){
40784             cls += ' ' + this.cls;
40785         }
40786         
40787         var cfg = {
40788             tag: (this.href.length) ? 'a' : 'div',
40789             cls: cls,
40790             cn: [
40791                 {
40792                     tag: 'div',
40793                     cls: 'masonry-brick-split-head',
40794                     cn: [
40795                         {
40796                             tag: 'div',
40797                             cls: 'masonry-brick-paragraph',
40798                             cn: []
40799                         }
40800                     ]
40801                 },
40802                 {
40803                     tag: 'div',
40804                     cls: 'masonry-brick-split-body',
40805                     cn: []
40806                 }
40807             ]
40808         };
40809         
40810         if(this.href.length){
40811             cfg.href = this.href;
40812         }
40813         
40814         if(this.title.length){
40815             cfg.cn[0].cn[0].cn.push({
40816                 tag: 'h4',
40817                 cls: 'masonry-brick-title',
40818                 html: this.title
40819             });
40820         }
40821         
40822         if(this.html.length){
40823             cfg.cn[1].cn.push({
40824                 tag: 'p',
40825                 cls: 'masonry-brick-text',
40826                 html: this.html
40827             });
40828         }
40829
40830         if(this.bgimage.length){
40831             cfg.cn[0].cn.push({
40832                 tag: 'img',
40833                 cls: 'masonry-brick-image-view',
40834                 src: this.bgimage
40835             });
40836         }
40837         
40838         if(this.videourl.length){
40839             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40840             // youtube support only?
40841             cfg.cn[0].cn.cn.push({
40842                 tag: 'iframe',
40843                 cls: 'masonry-brick-image-view',
40844                 src: vurl,
40845                 frameborder : 0,
40846                 allowfullscreen : true
40847             });
40848         }
40849         
40850         return cfg;
40851     },
40852     
40853     initEvents: function() 
40854     {
40855         switch (this.size) {
40856             case 'xs' :
40857                 this.x = 1;
40858                 this.y = 1;
40859                 break;
40860             case 'sm' :
40861                 this.x = 2;
40862                 this.y = 2;
40863                 break;
40864             case 'md' :
40865             case 'md-left' :
40866             case 'md-right' :
40867                 this.x = 3;
40868                 this.y = 3;
40869                 break;
40870             case 'tall' :
40871                 this.x = 2;
40872                 this.y = 3;
40873                 break;
40874             case 'wide' :
40875                 this.x = 3;
40876                 this.y = 2;
40877                 break;
40878             case 'wide-thin' :
40879                 this.x = 3;
40880                 this.y = 1;
40881                 break;
40882                         
40883             default :
40884                 break;
40885         }
40886         
40887         if(Roo.isTouch){
40888             this.el.on('touchstart', this.onTouchStart, this);
40889             this.el.on('touchmove', this.onTouchMove, this);
40890             this.el.on('touchend', this.onTouchEnd, this);
40891             this.el.on('contextmenu', this.onContextMenu, this);
40892         } else {
40893             this.el.on('mouseenter'  ,this.enter, this);
40894             this.el.on('mouseleave', this.leave, this);
40895             this.el.on('click', this.onClick, this);
40896         }
40897         
40898         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40899             this.parent().bricks.push(this);   
40900         }
40901         
40902     },
40903     
40904     onClick: function(e, el)
40905     {
40906         var time = this.endTimer - this.startTimer;
40907         // Roo.log(e.preventDefault());
40908         if(Roo.isTouch){
40909             if(time > 1000){
40910                 e.preventDefault();
40911                 return;
40912             }
40913         }
40914         
40915         if(!this.preventDefault){
40916             return;
40917         }
40918         
40919         e.preventDefault();
40920         
40921         if (this.activeClass != '') {
40922             this.selectBrick();
40923         }
40924         
40925         this.fireEvent('click', this, e);
40926     },
40927     
40928     enter: function(e, el)
40929     {
40930         e.preventDefault();
40931         
40932         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40933             return;
40934         }
40935         
40936         if(this.bgimage.length && this.html.length){
40937             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40938         }
40939     },
40940     
40941     leave: function(e, el)
40942     {
40943         e.preventDefault();
40944         
40945         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
40946             return;
40947         }
40948         
40949         if(this.bgimage.length && this.html.length){
40950             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40951         }
40952     },
40953     
40954     onTouchStart: function(e, el)
40955     {
40956 //        e.preventDefault();
40957         
40958         this.touchmoved = false;
40959         
40960         if(!this.isFitContainer){
40961             return;
40962         }
40963         
40964         if(!this.bgimage.length || !this.html.length){
40965             return;
40966         }
40967         
40968         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40969         
40970         this.timer = new Date().getTime();
40971         
40972     },
40973     
40974     onTouchMove: function(e, el)
40975     {
40976         this.touchmoved = true;
40977     },
40978     
40979     onContextMenu : function(e,el)
40980     {
40981         e.preventDefault();
40982         e.stopPropagation();
40983         return false;
40984     },
40985     
40986     onTouchEnd: function(e, el)
40987     {
40988 //        e.preventDefault();
40989         
40990         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40991         
40992             this.leave(e,el);
40993             
40994             return;
40995         }
40996         
40997         if(!this.bgimage.length || !this.html.length){
40998             
40999             if(this.href.length){
41000                 window.location.href = this.href;
41001             }
41002             
41003             return;
41004         }
41005         
41006         if(!this.isFitContainer){
41007             return;
41008         }
41009         
41010         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41011         
41012         window.location.href = this.href;
41013     },
41014     
41015     //selection on single brick only
41016     selectBrick : function() {
41017         
41018         if (!this.parentId) {
41019             return;
41020         }
41021         
41022         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41023         var index = m.selectedBrick.indexOf(this.id);
41024         
41025         if ( index > -1) {
41026             m.selectedBrick.splice(index,1);
41027             this.el.removeClass(this.activeClass);
41028             return;
41029         }
41030         
41031         for(var i = 0; i < m.selectedBrick.length; i++) {
41032             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41033             b.el.removeClass(b.activeClass);
41034         }
41035         
41036         m.selectedBrick = [];
41037         
41038         m.selectedBrick.push(this.id);
41039         this.el.addClass(this.activeClass);
41040         return;
41041     },
41042     
41043     isSelected : function(){
41044         return this.el.hasClass(this.activeClass);
41045         
41046     }
41047 });
41048
41049 Roo.apply(Roo.bootstrap.MasonryBrick, {
41050     
41051     //groups: {},
41052     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41053      /**
41054     * register a Masonry Brick
41055     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41056     */
41057     
41058     register : function(brick)
41059     {
41060         //this.groups[brick.id] = brick;
41061         this.groups.add(brick.id, brick);
41062     },
41063     /**
41064     * fetch a  masonry brick based on the masonry brick ID
41065     * @param {string} the masonry brick to add
41066     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41067     */
41068     
41069     get: function(brick_id) 
41070     {
41071         // if (typeof(this.groups[brick_id]) == 'undefined') {
41072         //     return false;
41073         // }
41074         // return this.groups[brick_id] ;
41075         
41076         if(this.groups.key(brick_id)) {
41077             return this.groups.key(brick_id);
41078         }
41079         
41080         return false;
41081     }
41082     
41083     
41084     
41085 });
41086
41087  /*
41088  * - LGPL
41089  *
41090  * element
41091  * 
41092  */
41093
41094 /**
41095  * @class Roo.bootstrap.Brick
41096  * @extends Roo.bootstrap.Component
41097  * Bootstrap Brick class
41098  * 
41099  * @constructor
41100  * Create a new Brick
41101  * @param {Object} config The config object
41102  */
41103
41104 Roo.bootstrap.Brick = function(config){
41105     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41106     
41107     this.addEvents({
41108         // raw events
41109         /**
41110          * @event click
41111          * When a Brick is click
41112          * @param {Roo.bootstrap.Brick} this
41113          * @param {Roo.EventObject} e
41114          */
41115         "click" : true
41116     });
41117 };
41118
41119 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
41120     
41121     /**
41122      * @cfg {String} title
41123      */   
41124     title : '',
41125     /**
41126      * @cfg {String} html
41127      */   
41128     html : '',
41129     /**
41130      * @cfg {String} bgimage
41131      */   
41132     bgimage : '',
41133     /**
41134      * @cfg {String} cls
41135      */   
41136     cls : '',
41137     /**
41138      * @cfg {String} href
41139      */   
41140     href : '',
41141     /**
41142      * @cfg {String} video
41143      */   
41144     video : '',
41145     /**
41146      * @cfg {Boolean} square
41147      */   
41148     square : true,
41149     
41150     getAutoCreate : function()
41151     {
41152         var cls = 'roo-brick';
41153         
41154         if(this.href.length){
41155             cls += ' roo-brick-link';
41156         }
41157         
41158         if(this.bgimage.length){
41159             cls += ' roo-brick-image';
41160         }
41161         
41162         if(!this.html.length && !this.bgimage.length){
41163             cls += ' roo-brick-center-title';
41164         }
41165         
41166         if(!this.html.length && this.bgimage.length){
41167             cls += ' roo-brick-bottom-title';
41168         }
41169         
41170         if(this.cls){
41171             cls += ' ' + this.cls;
41172         }
41173         
41174         var cfg = {
41175             tag: (this.href.length) ? 'a' : 'div',
41176             cls: cls,
41177             cn: [
41178                 {
41179                     tag: 'div',
41180                     cls: 'roo-brick-paragraph',
41181                     cn: []
41182                 }
41183             ]
41184         };
41185         
41186         if(this.href.length){
41187             cfg.href = this.href;
41188         }
41189         
41190         var cn = cfg.cn[0].cn;
41191         
41192         if(this.title.length){
41193             cn.push({
41194                 tag: 'h4',
41195                 cls: 'roo-brick-title',
41196                 html: this.title
41197             });
41198         }
41199         
41200         if(this.html.length){
41201             cn.push({
41202                 tag: 'p',
41203                 cls: 'roo-brick-text',
41204                 html: this.html
41205             });
41206         } else {
41207             cn.cls += ' hide';
41208         }
41209         
41210         if(this.bgimage.length){
41211             cfg.cn.push({
41212                 tag: 'img',
41213                 cls: 'roo-brick-image-view',
41214                 src: this.bgimage
41215             });
41216         }
41217         
41218         return cfg;
41219     },
41220     
41221     initEvents: function() 
41222     {
41223         if(this.title.length || this.html.length){
41224             this.el.on('mouseenter'  ,this.enter, this);
41225             this.el.on('mouseleave', this.leave, this);
41226         }
41227         
41228         Roo.EventManager.onWindowResize(this.resize, this); 
41229         
41230         if(this.bgimage.length){
41231             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41232             this.imageEl.on('load', this.onImageLoad, this);
41233             return;
41234         }
41235         
41236         this.resize();
41237     },
41238     
41239     onImageLoad : function()
41240     {
41241         this.resize();
41242     },
41243     
41244     resize : function()
41245     {
41246         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41247         
41248         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41249         
41250         if(this.bgimage.length){
41251             var image = this.el.select('.roo-brick-image-view', true).first();
41252             
41253             image.setWidth(paragraph.getWidth());
41254             
41255             if(this.square){
41256                 image.setHeight(paragraph.getWidth());
41257             }
41258             
41259             this.el.setHeight(image.getHeight());
41260             paragraph.setHeight(image.getHeight());
41261             
41262         }
41263         
41264     },
41265     
41266     enter: function(e, el)
41267     {
41268         e.preventDefault();
41269         
41270         if(this.bgimage.length){
41271             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41272             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41273         }
41274     },
41275     
41276     leave: function(e, el)
41277     {
41278         e.preventDefault();
41279         
41280         if(this.bgimage.length){
41281             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41282             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41283         }
41284     }
41285     
41286 });
41287
41288  
41289
41290  /*
41291  * - LGPL
41292  *
41293  * Number field 
41294  */
41295
41296 /**
41297  * @class Roo.bootstrap.form.NumberField
41298  * @extends Roo.bootstrap.form.Input
41299  * Bootstrap NumberField class
41300  * 
41301  * 
41302  * 
41303  * 
41304  * @constructor
41305  * Create a new NumberField
41306  * @param {Object} config The config object
41307  */
41308
41309 Roo.bootstrap.form.NumberField = function(config){
41310     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41311 };
41312
41313 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41314     
41315     /**
41316      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41317      */
41318     allowDecimals : true,
41319     /**
41320      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41321      */
41322     decimalSeparator : ".",
41323     /**
41324      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41325      */
41326     decimalPrecision : 2,
41327     /**
41328      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41329      */
41330     allowNegative : true,
41331     
41332     /**
41333      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41334      */
41335     allowZero: true,
41336     /**
41337      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41338      */
41339     minValue : Number.NEGATIVE_INFINITY,
41340     /**
41341      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41342      */
41343     maxValue : Number.MAX_VALUE,
41344     /**
41345      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41346      */
41347     minText : "The minimum value for this field is {0}",
41348     /**
41349      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41350      */
41351     maxText : "The maximum value for this field is {0}",
41352     /**
41353      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41354      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41355      */
41356     nanText : "{0} is not a valid number",
41357     /**
41358      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41359      */
41360     thousandsDelimiter : false,
41361     /**
41362      * @cfg {String} valueAlign alignment of value
41363      */
41364     valueAlign : "left",
41365
41366     getAutoCreate : function()
41367     {
41368         var hiddenInput = {
41369             tag: 'input',
41370             type: 'hidden',
41371             id: Roo.id(),
41372             cls: 'hidden-number-input'
41373         };
41374         
41375         if (this.name) {
41376             hiddenInput.name = this.name;
41377         }
41378         
41379         this.name = '';
41380         
41381         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41382         
41383         this.name = hiddenInput.name;
41384         
41385         if(cfg.cn.length > 0) {
41386             cfg.cn.push(hiddenInput);
41387         }
41388         
41389         return cfg;
41390     },
41391
41392     // private
41393     initEvents : function()
41394     {   
41395         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41396         
41397         var allowed = "0123456789";
41398         
41399         if(this.allowDecimals){
41400             allowed += this.decimalSeparator;
41401         }
41402         
41403         if(this.allowNegative){
41404             allowed += "-";
41405         }
41406         
41407         if(this.thousandsDelimiter) {
41408             allowed += ",";
41409         }
41410         
41411         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41412         
41413         var keyPress = function(e){
41414             
41415             var k = e.getKey();
41416             
41417             var c = e.getCharCode();
41418             
41419             if(
41420                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41421                     allowed.indexOf(String.fromCharCode(c)) === -1
41422             ){
41423                 e.stopEvent();
41424                 return;
41425             }
41426             
41427             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41428                 return;
41429             }
41430             
41431             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41432                 e.stopEvent();
41433             }
41434         };
41435         
41436         this.el.on("keypress", keyPress, this);
41437     },
41438     
41439     validateValue : function(value)
41440     {
41441         
41442         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41443             return false;
41444         }
41445         
41446         var num = this.parseValue(value);
41447         
41448         if(isNaN(num)){
41449             this.markInvalid(String.format(this.nanText, value));
41450             return false;
41451         }
41452         
41453         if(num < this.minValue){
41454             this.markInvalid(String.format(this.minText, this.minValue));
41455             return false;
41456         }
41457         
41458         if(num > this.maxValue){
41459             this.markInvalid(String.format(this.maxText, this.maxValue));
41460             return false;
41461         }
41462         
41463         return true;
41464     },
41465
41466     getValue : function()
41467     {
41468         var v = this.hiddenEl().getValue();
41469         
41470         return this.fixPrecision(this.parseValue(v));
41471     },
41472
41473     parseValue : function(value)
41474     {
41475         if(this.thousandsDelimiter) {
41476             value += "";
41477             r = new RegExp(",", "g");
41478             value = value.replace(r, "");
41479         }
41480         
41481         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41482         return isNaN(value) ? '' : value;
41483     },
41484
41485     fixPrecision : function(value)
41486     {
41487         if(this.thousandsDelimiter) {
41488             value += "";
41489             r = new RegExp(",", "g");
41490             value = value.replace(r, "");
41491         }
41492         
41493         var nan = isNaN(value);
41494         
41495         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41496             return nan ? '' : value;
41497         }
41498         return parseFloat(value).toFixed(this.decimalPrecision);
41499     },
41500
41501     setValue : function(v)
41502     {
41503         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41504         
41505         this.value = v;
41506         
41507         if(this.rendered){
41508             
41509             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41510             
41511             this.inputEl().dom.value = (v == '') ? '' :
41512                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41513             
41514             if(!this.allowZero && v === '0') {
41515                 this.hiddenEl().dom.value = '';
41516                 this.inputEl().dom.value = '';
41517             }
41518             
41519             this.validate();
41520         }
41521     },
41522
41523     decimalPrecisionFcn : function(v)
41524     {
41525         return Math.floor(v);
41526     },
41527
41528     beforeBlur : function()
41529     {
41530         var v = this.parseValue(this.getRawValue());
41531         
41532         if(v || v === 0 || v === ''){
41533             this.setValue(v);
41534         }
41535     },
41536     
41537     hiddenEl : function()
41538     {
41539         return this.el.select('input.hidden-number-input',true).first();
41540     }
41541     
41542 });
41543
41544  
41545
41546 /*
41547 * Licence: LGPL
41548 */
41549
41550 /**
41551  * @class Roo.bootstrap.DocumentSlider
41552  * @extends Roo.bootstrap.Component
41553  * Bootstrap DocumentSlider class
41554  * 
41555  * @constructor
41556  * Create a new DocumentViewer
41557  * @param {Object} config The config object
41558  */
41559
41560 Roo.bootstrap.DocumentSlider = function(config){
41561     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41562     
41563     this.files = [];
41564     
41565     this.addEvents({
41566         /**
41567          * @event initial
41568          * Fire after initEvent
41569          * @param {Roo.bootstrap.DocumentSlider} this
41570          */
41571         "initial" : true,
41572         /**
41573          * @event update
41574          * Fire after update
41575          * @param {Roo.bootstrap.DocumentSlider} this
41576          */
41577         "update" : true,
41578         /**
41579          * @event click
41580          * Fire after click
41581          * @param {Roo.bootstrap.DocumentSlider} this
41582          */
41583         "click" : true
41584     });
41585 };
41586
41587 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41588     
41589     files : false,
41590     
41591     indicator : 0,
41592     
41593     getAutoCreate : function()
41594     {
41595         var cfg = {
41596             tag : 'div',
41597             cls : 'roo-document-slider',
41598             cn : [
41599                 {
41600                     tag : 'div',
41601                     cls : 'roo-document-slider-header',
41602                     cn : [
41603                         {
41604                             tag : 'div',
41605                             cls : 'roo-document-slider-header-title'
41606                         }
41607                     ]
41608                 },
41609                 {
41610                     tag : 'div',
41611                     cls : 'roo-document-slider-body',
41612                     cn : [
41613                         {
41614                             tag : 'div',
41615                             cls : 'roo-document-slider-prev',
41616                             cn : [
41617                                 {
41618                                     tag : 'i',
41619                                     cls : 'fa fa-chevron-left'
41620                                 }
41621                             ]
41622                         },
41623                         {
41624                             tag : 'div',
41625                             cls : 'roo-document-slider-thumb',
41626                             cn : [
41627                                 {
41628                                     tag : 'img',
41629                                     cls : 'roo-document-slider-image'
41630                                 }
41631                             ]
41632                         },
41633                         {
41634                             tag : 'div',
41635                             cls : 'roo-document-slider-next',
41636                             cn : [
41637                                 {
41638                                     tag : 'i',
41639                                     cls : 'fa fa-chevron-right'
41640                                 }
41641                             ]
41642                         }
41643                     ]
41644                 }
41645             ]
41646         };
41647         
41648         return cfg;
41649     },
41650     
41651     initEvents : function()
41652     {
41653         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41654         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41655         
41656         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41657         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41658         
41659         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41660         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41661         
41662         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41663         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41664         
41665         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41666         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41667         
41668         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41669         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41670         
41671         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41672         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41673         
41674         this.thumbEl.on('click', this.onClick, this);
41675         
41676         this.prevIndicator.on('click', this.prev, this);
41677         
41678         this.nextIndicator.on('click', this.next, this);
41679         
41680     },
41681     
41682     initial : function()
41683     {
41684         if(this.files.length){
41685             this.indicator = 1;
41686             this.update()
41687         }
41688         
41689         this.fireEvent('initial', this);
41690     },
41691     
41692     update : function()
41693     {
41694         this.imageEl.attr('src', this.files[this.indicator - 1]);
41695         
41696         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41697         
41698         this.prevIndicator.show();
41699         
41700         if(this.indicator == 1){
41701             this.prevIndicator.hide();
41702         }
41703         
41704         this.nextIndicator.show();
41705         
41706         if(this.indicator == this.files.length){
41707             this.nextIndicator.hide();
41708         }
41709         
41710         this.thumbEl.scrollTo('top');
41711         
41712         this.fireEvent('update', this);
41713     },
41714     
41715     onClick : function(e)
41716     {
41717         e.preventDefault();
41718         
41719         this.fireEvent('click', this);
41720     },
41721     
41722     prev : function(e)
41723     {
41724         e.preventDefault();
41725         
41726         this.indicator = Math.max(1, this.indicator - 1);
41727         
41728         this.update();
41729     },
41730     
41731     next : function(e)
41732     {
41733         e.preventDefault();
41734         
41735         this.indicator = Math.min(this.files.length, this.indicator + 1);
41736         
41737         this.update();
41738     }
41739 });
41740 /*
41741  * - LGPL
41742  *
41743  * RadioSet
41744  *
41745  *
41746  */
41747
41748 /**
41749  * @class Roo.bootstrap.form.RadioSet
41750  * @extends Roo.bootstrap.form.Input
41751  * @children Roo.bootstrap.form.Radio
41752  * Bootstrap RadioSet class
41753  * @cfg {String} indicatorpos (left|right) default left
41754  * @cfg {Boolean} inline (true|false) inline the element (default true)
41755  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41756  * @constructor
41757  * Create a new RadioSet
41758  * @param {Object} config The config object
41759  */
41760
41761 Roo.bootstrap.form.RadioSet = function(config){
41762     
41763     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41764     
41765     this.radioes = [];
41766     
41767     Roo.bootstrap.form.RadioSet.register(this);
41768     
41769     this.addEvents({
41770         /**
41771         * @event check
41772         * Fires when the element is checked or unchecked.
41773         * @param {Roo.bootstrap.form.RadioSet} this This radio
41774         * @param {Roo.bootstrap.form.Radio} item The checked item
41775         */
41776        check : true,
41777        /**
41778         * @event click
41779         * Fires when the element is click.
41780         * @param {Roo.bootstrap.form.RadioSet} this This radio set
41781         * @param {Roo.bootstrap.form.Radio} item The checked item
41782         * @param {Roo.EventObject} e The event object
41783         */
41784        click : true
41785     });
41786     
41787 };
41788
41789 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
41790
41791     radioes : false,
41792     
41793     inline : true,
41794     
41795     weight : '',
41796     
41797     indicatorpos : 'left',
41798     
41799     getAutoCreate : function()
41800     {
41801         var label = {
41802             tag : 'label',
41803             cls : 'roo-radio-set-label',
41804             cn : [
41805                 {
41806                     tag : 'span',
41807                     html : this.fieldLabel
41808                 }
41809             ]
41810         };
41811         if (Roo.bootstrap.version == 3) {
41812             
41813             
41814             if(this.indicatorpos == 'left'){
41815                 label.cn.unshift({
41816                     tag : 'i',
41817                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41818                     tooltip : 'This field is required'
41819                 });
41820             } else {
41821                 label.cn.push({
41822                     tag : 'i',
41823                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41824                     tooltip : 'This field is required'
41825                 });
41826             }
41827         }
41828         var items = {
41829             tag : 'div',
41830             cls : 'roo-radio-set-items'
41831         };
41832         
41833         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41834         
41835         if (align === 'left' && this.fieldLabel.length) {
41836             
41837             items = {
41838                 cls : "roo-radio-set-right", 
41839                 cn: [
41840                     items
41841                 ]
41842             };
41843             
41844             if(this.labelWidth > 12){
41845                 label.style = "width: " + this.labelWidth + 'px';
41846             }
41847             
41848             if(this.labelWidth < 13 && this.labelmd == 0){
41849                 this.labelmd = this.labelWidth;
41850             }
41851             
41852             if(this.labellg > 0){
41853                 label.cls += ' col-lg-' + this.labellg;
41854                 items.cls += ' col-lg-' + (12 - this.labellg);
41855             }
41856             
41857             if(this.labelmd > 0){
41858                 label.cls += ' col-md-' + this.labelmd;
41859                 items.cls += ' col-md-' + (12 - this.labelmd);
41860             }
41861             
41862             if(this.labelsm > 0){
41863                 label.cls += ' col-sm-' + this.labelsm;
41864                 items.cls += ' col-sm-' + (12 - this.labelsm);
41865             }
41866             
41867             if(this.labelxs > 0){
41868                 label.cls += ' col-xs-' + this.labelxs;
41869                 items.cls += ' col-xs-' + (12 - this.labelxs);
41870             }
41871         }
41872         
41873         var cfg = {
41874             tag : 'div',
41875             cls : 'roo-radio-set',
41876             cn : [
41877                 {
41878                     tag : 'input',
41879                     cls : 'roo-radio-set-input',
41880                     type : 'hidden',
41881                     name : this.name,
41882                     value : this.value ? this.value :  ''
41883                 },
41884                 label,
41885                 items
41886             ]
41887         };
41888         
41889         if(this.weight.length){
41890             cfg.cls += ' roo-radio-' + this.weight;
41891         }
41892         
41893         if(this.inline) {
41894             cfg.cls += ' roo-radio-set-inline';
41895         }
41896         
41897         var settings=this;
41898         ['xs','sm','md','lg'].map(function(size){
41899             if (settings[size]) {
41900                 cfg.cls += ' col-' + size + '-' + settings[size];
41901             }
41902         });
41903         
41904         return cfg;
41905         
41906     },
41907
41908     initEvents : function()
41909     {
41910         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41911         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41912         
41913         if(!this.fieldLabel.length){
41914             this.labelEl.hide();
41915         }
41916         
41917         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41918         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41919         
41920         this.indicator = this.indicatorEl();
41921         
41922         if(this.indicator){
41923             this.indicator.addClass('invisible');
41924         }
41925         
41926         this.originalValue = this.getValue();
41927         
41928     },
41929     
41930     inputEl: function ()
41931     {
41932         return this.el.select('.roo-radio-set-input', true).first();
41933     },
41934     
41935     getChildContainer : function()
41936     {
41937         return this.itemsEl;
41938     },
41939     
41940     register : function(item)
41941     {
41942         this.radioes.push(item);
41943         
41944     },
41945     
41946     validate : function()
41947     {   
41948         if(this.getVisibilityEl().hasClass('hidden')){
41949             return true;
41950         }
41951         
41952         var valid = false;
41953         
41954         Roo.each(this.radioes, function(i){
41955             if(!i.checked){
41956                 return;
41957             }
41958             
41959             valid = true;
41960             return false;
41961         });
41962         
41963         if(this.allowBlank) {
41964             return true;
41965         }
41966         
41967         if(this.disabled || valid){
41968             this.markValid();
41969             return true;
41970         }
41971         
41972         this.markInvalid();
41973         return false;
41974         
41975     },
41976     
41977     markValid : function()
41978     {
41979         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41980             this.indicatorEl().removeClass('visible');
41981             this.indicatorEl().addClass('invisible');
41982         }
41983         
41984         
41985         if (Roo.bootstrap.version == 3) {
41986             this.el.removeClass([this.invalidClass, this.validClass]);
41987             this.el.addClass(this.validClass);
41988         } else {
41989             this.el.removeClass(['is-invalid','is-valid']);
41990             this.el.addClass(['is-valid']);
41991         }
41992         this.fireEvent('valid', this);
41993     },
41994     
41995     markInvalid : function(msg)
41996     {
41997         if(this.allowBlank || this.disabled){
41998             return;
41999         }
42000         
42001         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42002             this.indicatorEl().removeClass('invisible');
42003             this.indicatorEl().addClass('visible');
42004         }
42005         if (Roo.bootstrap.version == 3) {
42006             this.el.removeClass([this.invalidClass, this.validClass]);
42007             this.el.addClass(this.invalidClass);
42008         } else {
42009             this.el.removeClass(['is-invalid','is-valid']);
42010             this.el.addClass(['is-invalid']);
42011         }
42012         
42013         this.fireEvent('invalid', this, msg);
42014         
42015     },
42016     
42017     setValue : function(v, suppressEvent)
42018     {   
42019         if(this.value === v){
42020             return;
42021         }
42022         
42023         this.value = v;
42024         
42025         if(this.rendered){
42026             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42027         }
42028         
42029         Roo.each(this.radioes, function(i){
42030             i.checked = false;
42031             i.el.removeClass('checked');
42032         });
42033         
42034         Roo.each(this.radioes, function(i){
42035             
42036             if(i.value === v || i.value.toString() === v.toString()){
42037                 i.checked = true;
42038                 i.el.addClass('checked');
42039                 
42040                 if(suppressEvent !== true){
42041                     this.fireEvent('check', this, i);
42042                 }
42043                 
42044                 return false;
42045             }
42046             
42047         }, this);
42048         
42049         this.validate();
42050     },
42051     
42052     clearInvalid : function(){
42053         
42054         if(!this.el || this.preventMark){
42055             return;
42056         }
42057         
42058         this.el.removeClass([this.invalidClass]);
42059         
42060         this.fireEvent('valid', this);
42061     }
42062     
42063 });
42064
42065 Roo.apply(Roo.bootstrap.form.RadioSet, {
42066     
42067     groups: {},
42068     
42069     register : function(set)
42070     {
42071         this.groups[set.name] = set;
42072     },
42073     
42074     get: function(name) 
42075     {
42076         if (typeof(this.groups[name]) == 'undefined') {
42077             return false;
42078         }
42079         
42080         return this.groups[name] ;
42081     }
42082     
42083 });
42084 /*
42085  * Based on:
42086  * Ext JS Library 1.1.1
42087  * Copyright(c) 2006-2007, Ext JS, LLC.
42088  *
42089  * Originally Released Under LGPL - original licence link has changed is not relivant.
42090  *
42091  * Fork - LGPL
42092  * <script type="text/javascript">
42093  */
42094
42095
42096 /**
42097  * @class Roo.bootstrap.SplitBar
42098  * @extends Roo.util.Observable
42099  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42100  * <br><br>
42101  * Usage:
42102  * <pre><code>
42103 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42104                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42105 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42106 split.minSize = 100;
42107 split.maxSize = 600;
42108 split.animate = true;
42109 split.on('moved', splitterMoved);
42110 </code></pre>
42111  * @constructor
42112  * Create a new SplitBar
42113  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
42114  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
42115  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42116  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
42117                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42118                         position of the SplitBar).
42119  */
42120 Roo.bootstrap.SplitBar = function(cfg){
42121     
42122     /** @private */
42123     
42124     //{
42125     //  dragElement : elm
42126     //  resizingElement: el,
42127         // optional..
42128     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42129     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
42130         // existingProxy ???
42131     //}
42132     
42133     this.el = Roo.get(cfg.dragElement, true);
42134     this.el.dom.unselectable = "on";
42135     /** @private */
42136     this.resizingEl = Roo.get(cfg.resizingElement, true);
42137
42138     /**
42139      * @private
42140      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42141      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42142      * @type Number
42143      */
42144     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42145     
42146     /**
42147      * The minimum size of the resizing element. (Defaults to 0)
42148      * @type Number
42149      */
42150     this.minSize = 0;
42151     
42152     /**
42153      * The maximum size of the resizing element. (Defaults to 2000)
42154      * @type Number
42155      */
42156     this.maxSize = 2000;
42157     
42158     /**
42159      * Whether to animate the transition to the new size
42160      * @type Boolean
42161      */
42162     this.animate = false;
42163     
42164     /**
42165      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42166      * @type Boolean
42167      */
42168     this.useShim = false;
42169     
42170     /** @private */
42171     this.shim = null;
42172     
42173     if(!cfg.existingProxy){
42174         /** @private */
42175         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42176     }else{
42177         this.proxy = Roo.get(cfg.existingProxy).dom;
42178     }
42179     /** @private */
42180     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42181     
42182     /** @private */
42183     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42184     
42185     /** @private */
42186     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42187     
42188     /** @private */
42189     this.dragSpecs = {};
42190     
42191     /**
42192      * @private The adapter to use to positon and resize elements
42193      */
42194     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42195     this.adapter.init(this);
42196     
42197     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42198         /** @private */
42199         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42200         this.el.addClass("roo-splitbar-h");
42201     }else{
42202         /** @private */
42203         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42204         this.el.addClass("roo-splitbar-v");
42205     }
42206     
42207     this.addEvents({
42208         /**
42209          * @event resize
42210          * Fires when the splitter is moved (alias for {@link #event-moved})
42211          * @param {Roo.bootstrap.SplitBar} this
42212          * @param {Number} newSize the new width or height
42213          */
42214         "resize" : true,
42215         /**
42216          * @event moved
42217          * Fires when the splitter is moved
42218          * @param {Roo.bootstrap.SplitBar} this
42219          * @param {Number} newSize the new width or height
42220          */
42221         "moved" : true,
42222         /**
42223          * @event beforeresize
42224          * Fires before the splitter is dragged
42225          * @param {Roo.bootstrap.SplitBar} this
42226          */
42227         "beforeresize" : true,
42228
42229         "beforeapply" : true
42230     });
42231
42232     Roo.util.Observable.call(this);
42233 };
42234
42235 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42236     onStartProxyDrag : function(x, y){
42237         this.fireEvent("beforeresize", this);
42238         if(!this.overlay){
42239             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
42240             o.unselectable();
42241             o.enableDisplayMode("block");
42242             // all splitbars share the same overlay
42243             Roo.bootstrap.SplitBar.prototype.overlay = o;
42244         }
42245         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42246         this.overlay.show();
42247         Roo.get(this.proxy).setDisplayed("block");
42248         var size = this.adapter.getElementSize(this);
42249         this.activeMinSize = this.getMinimumSize();;
42250         this.activeMaxSize = this.getMaximumSize();;
42251         var c1 = size - this.activeMinSize;
42252         var c2 = Math.max(this.activeMaxSize - size, 0);
42253         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42254             this.dd.resetConstraints();
42255             this.dd.setXConstraint(
42256                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
42257                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42258             );
42259             this.dd.setYConstraint(0, 0);
42260         }else{
42261             this.dd.resetConstraints();
42262             this.dd.setXConstraint(0, 0);
42263             this.dd.setYConstraint(
42264                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
42265                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42266             );
42267          }
42268         this.dragSpecs.startSize = size;
42269         this.dragSpecs.startPoint = [x, y];
42270         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42271     },
42272     
42273     /** 
42274      * @private Called after the drag operation by the DDProxy
42275      */
42276     onEndProxyDrag : function(e){
42277         Roo.get(this.proxy).setDisplayed(false);
42278         var endPoint = Roo.lib.Event.getXY(e);
42279         if(this.overlay){
42280             this.overlay.hide();
42281         }
42282         var newSize;
42283         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42284             newSize = this.dragSpecs.startSize + 
42285                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42286                     endPoint[0] - this.dragSpecs.startPoint[0] :
42287                     this.dragSpecs.startPoint[0] - endPoint[0]
42288                 );
42289         }else{
42290             newSize = this.dragSpecs.startSize + 
42291                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42292                     endPoint[1] - this.dragSpecs.startPoint[1] :
42293                     this.dragSpecs.startPoint[1] - endPoint[1]
42294                 );
42295         }
42296         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42297         if(newSize != this.dragSpecs.startSize){
42298             if(this.fireEvent('beforeapply', this, newSize) !== false){
42299                 this.adapter.setElementSize(this, newSize);
42300                 this.fireEvent("moved", this, newSize);
42301                 this.fireEvent("resize", this, newSize);
42302             }
42303         }
42304     },
42305     
42306     /**
42307      * Get the adapter this SplitBar uses
42308      * @return The adapter object
42309      */
42310     getAdapter : function(){
42311         return this.adapter;
42312     },
42313     
42314     /**
42315      * Set the adapter this SplitBar uses
42316      * @param {Object} adapter A SplitBar adapter object
42317      */
42318     setAdapter : function(adapter){
42319         this.adapter = adapter;
42320         this.adapter.init(this);
42321     },
42322     
42323     /**
42324      * Gets the minimum size for the resizing element
42325      * @return {Number} The minimum size
42326      */
42327     getMinimumSize : function(){
42328         return this.minSize;
42329     },
42330     
42331     /**
42332      * Sets the minimum size for the resizing element
42333      * @param {Number} minSize The minimum size
42334      */
42335     setMinimumSize : function(minSize){
42336         this.minSize = minSize;
42337     },
42338     
42339     /**
42340      * Gets the maximum size for the resizing element
42341      * @return {Number} The maximum size
42342      */
42343     getMaximumSize : function(){
42344         return this.maxSize;
42345     },
42346     
42347     /**
42348      * Sets the maximum size for the resizing element
42349      * @param {Number} maxSize The maximum size
42350      */
42351     setMaximumSize : function(maxSize){
42352         this.maxSize = maxSize;
42353     },
42354     
42355     /**
42356      * Sets the initialize size for the resizing element
42357      * @param {Number} size The initial size
42358      */
42359     setCurrentSize : function(size){
42360         var oldAnimate = this.animate;
42361         this.animate = false;
42362         this.adapter.setElementSize(this, size);
42363         this.animate = oldAnimate;
42364     },
42365     
42366     /**
42367      * Destroy this splitbar. 
42368      * @param {Boolean} removeEl True to remove the element
42369      */
42370     destroy : function(removeEl){
42371         if(this.shim){
42372             this.shim.remove();
42373         }
42374         this.dd.unreg();
42375         this.proxy.parentNode.removeChild(this.proxy);
42376         if(removeEl){
42377             this.el.remove();
42378         }
42379     }
42380 });
42381
42382 /**
42383  * @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.
42384  */
42385 Roo.bootstrap.SplitBar.createProxy = function(dir){
42386     var proxy = new Roo.Element(document.createElement("div"));
42387     proxy.unselectable();
42388     var cls = 'roo-splitbar-proxy';
42389     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42390     document.body.appendChild(proxy.dom);
42391     return proxy.dom;
42392 };
42393
42394 /** 
42395  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42396  * Default Adapter. It assumes the splitter and resizing element are not positioned
42397  * elements and only gets/sets the width of the element. Generally used for table based layouts.
42398  */
42399 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42400 };
42401
42402 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42403     // do nothing for now
42404     init : function(s){
42405     
42406     },
42407     /**
42408      * Called before drag operations to get the current size of the resizing element. 
42409      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42410      */
42411      getElementSize : function(s){
42412         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42413             return s.resizingEl.getWidth();
42414         }else{
42415             return s.resizingEl.getHeight();
42416         }
42417     },
42418     
42419     /**
42420      * Called after drag operations to set the size of the resizing element.
42421      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42422      * @param {Number} newSize The new size to set
42423      * @param {Function} onComplete A function to be invoked when resizing is complete
42424      */
42425     setElementSize : function(s, newSize, onComplete){
42426         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42427             if(!s.animate){
42428                 s.resizingEl.setWidth(newSize);
42429                 if(onComplete){
42430                     onComplete(s, newSize);
42431                 }
42432             }else{
42433                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42434             }
42435         }else{
42436             
42437             if(!s.animate){
42438                 s.resizingEl.setHeight(newSize);
42439                 if(onComplete){
42440                     onComplete(s, newSize);
42441                 }
42442             }else{
42443                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42444             }
42445         }
42446     }
42447 };
42448
42449 /** 
42450  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42451  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42452  * Adapter that  moves the splitter element to align with the resized sizing element. 
42453  * Used with an absolute positioned SplitBar.
42454  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42455  * document.body, make sure you assign an id to the body element.
42456  */
42457 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42458     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42459     this.container = Roo.get(container);
42460 };
42461
42462 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42463     init : function(s){
42464         this.basic.init(s);
42465     },
42466     
42467     getElementSize : function(s){
42468         return this.basic.getElementSize(s);
42469     },
42470     
42471     setElementSize : function(s, newSize, onComplete){
42472         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42473     },
42474     
42475     moveSplitter : function(s){
42476         var yes = Roo.bootstrap.SplitBar;
42477         switch(s.placement){
42478             case yes.LEFT:
42479                 s.el.setX(s.resizingEl.getRight());
42480                 break;
42481             case yes.RIGHT:
42482                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42483                 break;
42484             case yes.TOP:
42485                 s.el.setY(s.resizingEl.getBottom());
42486                 break;
42487             case yes.BOTTOM:
42488                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42489                 break;
42490         }
42491     }
42492 };
42493
42494 /**
42495  * Orientation constant - Create a vertical SplitBar
42496  * @static
42497  * @type Number
42498  */
42499 Roo.bootstrap.SplitBar.VERTICAL = 1;
42500
42501 /**
42502  * Orientation constant - Create a horizontal SplitBar
42503  * @static
42504  * @type Number
42505  */
42506 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42507
42508 /**
42509  * Placement constant - The resizing element is to the left of the splitter element
42510  * @static
42511  * @type Number
42512  */
42513 Roo.bootstrap.SplitBar.LEFT = 1;
42514
42515 /**
42516  * Placement constant - The resizing element is to the right of the splitter element
42517  * @static
42518  * @type Number
42519  */
42520 Roo.bootstrap.SplitBar.RIGHT = 2;
42521
42522 /**
42523  * Placement constant - The resizing element is positioned above the splitter element
42524  * @static
42525  * @type Number
42526  */
42527 Roo.bootstrap.SplitBar.TOP = 3;
42528
42529 /**
42530  * Placement constant - The resizing element is positioned under splitter element
42531  * @static
42532  * @type Number
42533  */
42534 Roo.bootstrap.SplitBar.BOTTOM = 4;
42535 /*
42536  * Based on:
42537  * Ext JS Library 1.1.1
42538  * Copyright(c) 2006-2007, Ext JS, LLC.
42539  *
42540  * Originally Released Under LGPL - original licence link has changed is not relivant.
42541  *
42542  * Fork - LGPL
42543  * <script type="text/javascript">
42544  */
42545
42546 /**
42547  * @class Roo.bootstrap.layout.Manager
42548  * @extends Roo.bootstrap.Component
42549  * @abstract
42550  * Base class for layout managers.
42551  */
42552 Roo.bootstrap.layout.Manager = function(config)
42553 {
42554     this.monitorWindowResize = true; // do this before we apply configuration.
42555     
42556     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42557
42558
42559
42560
42561
42562     /** false to disable window resize monitoring @type Boolean */
42563     
42564     this.regions = {};
42565     this.addEvents({
42566         /**
42567          * @event layout
42568          * Fires when a layout is performed.
42569          * @param {Roo.LayoutManager} this
42570          */
42571         "layout" : true,
42572         /**
42573          * @event regionresized
42574          * Fires when the user resizes a region.
42575          * @param {Roo.LayoutRegion} region The resized region
42576          * @param {Number} newSize The new size (width for east/west, height for north/south)
42577          */
42578         "regionresized" : true,
42579         /**
42580          * @event regioncollapsed
42581          * Fires when a region is collapsed.
42582          * @param {Roo.LayoutRegion} region The collapsed region
42583          */
42584         "regioncollapsed" : true,
42585         /**
42586          * @event regionexpanded
42587          * Fires when a region is expanded.
42588          * @param {Roo.LayoutRegion} region The expanded region
42589          */
42590         "regionexpanded" : true
42591     });
42592     this.updating = false;
42593
42594     if (config.el) {
42595         this.el = Roo.get(config.el);
42596         this.initEvents();
42597     }
42598
42599 };
42600
42601 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42602
42603
42604     regions : null,
42605
42606     monitorWindowResize : true,
42607
42608
42609     updating : false,
42610
42611
42612     onRender : function(ct, position)
42613     {
42614         if(!this.el){
42615             this.el = Roo.get(ct);
42616             this.initEvents();
42617         }
42618         //this.fireEvent('render',this);
42619     },
42620
42621
42622     initEvents: function()
42623     {
42624
42625
42626         // ie scrollbar fix
42627         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42628             document.body.scroll = "no";
42629         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42630             this.el.position('relative');
42631         }
42632         this.id = this.el.id;
42633         this.el.addClass("roo-layout-container");
42634         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42635         if(this.el.dom != document.body ) {
42636             this.el.on('resize', this.layout,this);
42637             this.el.on('show', this.layout,this);
42638         }
42639
42640     },
42641
42642     /**
42643      * Returns true if this layout is currently being updated
42644      * @return {Boolean}
42645      */
42646     isUpdating : function(){
42647         return this.updating;
42648     },
42649
42650     /**
42651      * Suspend the LayoutManager from doing auto-layouts while
42652      * making multiple add or remove calls
42653      */
42654     beginUpdate : function(){
42655         this.updating = true;
42656     },
42657
42658     /**
42659      * Restore auto-layouts and optionally disable the manager from performing a layout
42660      * @param {Boolean} noLayout true to disable a layout update
42661      */
42662     endUpdate : function(noLayout){
42663         this.updating = false;
42664         if(!noLayout){
42665             this.layout();
42666         }
42667     },
42668
42669     layout: function(){
42670         // abstract...
42671     },
42672
42673     onRegionResized : function(region, newSize){
42674         this.fireEvent("regionresized", region, newSize);
42675         this.layout();
42676     },
42677
42678     onRegionCollapsed : function(region){
42679         this.fireEvent("regioncollapsed", region);
42680     },
42681
42682     onRegionExpanded : function(region){
42683         this.fireEvent("regionexpanded", region);
42684     },
42685
42686     /**
42687      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42688      * performs box-model adjustments.
42689      * @return {Object} The size as an object {width: (the width), height: (the height)}
42690      */
42691     getViewSize : function()
42692     {
42693         var size;
42694         if(this.el.dom != document.body){
42695             size = this.el.getSize();
42696         }else{
42697             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42698         }
42699         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42700         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42701         return size;
42702     },
42703
42704     /**
42705      * Returns the Element this layout is bound to.
42706      * @return {Roo.Element}
42707      */
42708     getEl : function(){
42709         return this.el;
42710     },
42711
42712     /**
42713      * Returns the specified region.
42714      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42715      * @return {Roo.LayoutRegion}
42716      */
42717     getRegion : function(target){
42718         return this.regions[target.toLowerCase()];
42719     },
42720
42721     onWindowResize : function(){
42722         if(this.monitorWindowResize){
42723             this.layout();
42724         }
42725     }
42726 });
42727 /*
42728  * Based on:
42729  * Ext JS Library 1.1.1
42730  * Copyright(c) 2006-2007, Ext JS, LLC.
42731  *
42732  * Originally Released Under LGPL - original licence link has changed is not relivant.
42733  *
42734  * Fork - LGPL
42735  * <script type="text/javascript">
42736  */
42737 /**
42738  * @class Roo.bootstrap.layout.Border
42739  * @extends Roo.bootstrap.layout.Manager
42740  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42741  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42742  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42743  * please see: examples/bootstrap/nested.html<br><br>
42744  
42745 <b>The container the layout is rendered into can be either the body element or any other element.
42746 If it is not the body element, the container needs to either be an absolute positioned element,
42747 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42748 the container size if it is not the body element.</b>
42749
42750 * @constructor
42751 * Create a new Border
42752 * @param {Object} config Configuration options
42753  */
42754 Roo.bootstrap.layout.Border = function(config){
42755     config = config || {};
42756     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42757     
42758     
42759     
42760     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42761         if(config[region]){
42762             config[region].region = region;
42763             this.addRegion(config[region]);
42764         }
42765     },this);
42766     
42767 };
42768
42769 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
42770
42771 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42772     
42773         /**
42774          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42775          */
42776         /**
42777          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42778          */
42779         /**
42780          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42781          */
42782         /**
42783          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42784          */
42785         /**
42786          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42787          */
42788         
42789         
42790         
42791         
42792     parent : false, // this might point to a 'nest' or a ???
42793     
42794     /**
42795      * Creates and adds a new region if it doesn't already exist.
42796      * @param {String} target The target region key (north, south, east, west or center).
42797      * @param {Object} config The regions config object
42798      * @return {BorderLayoutRegion} The new region
42799      */
42800     addRegion : function(config)
42801     {
42802         if(!this.regions[config.region]){
42803             var r = this.factory(config);
42804             this.bindRegion(r);
42805         }
42806         return this.regions[config.region];
42807     },
42808
42809     // private (kinda)
42810     bindRegion : function(r){
42811         this.regions[r.config.region] = r;
42812         
42813         r.on("visibilitychange",    this.layout, this);
42814         r.on("paneladded",          this.layout, this);
42815         r.on("panelremoved",        this.layout, this);
42816         r.on("invalidated",         this.layout, this);
42817         r.on("resized",             this.onRegionResized, this);
42818         r.on("collapsed",           this.onRegionCollapsed, this);
42819         r.on("expanded",            this.onRegionExpanded, this);
42820     },
42821
42822     /**
42823      * Performs a layout update.
42824      */
42825     layout : function()
42826     {
42827         if(this.updating) {
42828             return;
42829         }
42830         
42831         // render all the rebions if they have not been done alreayd?
42832         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42833             if(this.regions[region] && !this.regions[region].bodyEl){
42834                 this.regions[region].onRender(this.el)
42835             }
42836         },this);
42837         
42838         var size = this.getViewSize();
42839         var w = size.width;
42840         var h = size.height;
42841         var centerW = w;
42842         var centerH = h;
42843         var centerY = 0;
42844         var centerX = 0;
42845         //var x = 0, y = 0;
42846
42847         var rs = this.regions;
42848         var north = rs["north"];
42849         var south = rs["south"]; 
42850         var west = rs["west"];
42851         var east = rs["east"];
42852         var center = rs["center"];
42853         //if(this.hideOnLayout){ // not supported anymore
42854             //c.el.setStyle("display", "none");
42855         //}
42856         if(north && north.isVisible()){
42857             var b = north.getBox();
42858             var m = north.getMargins();
42859             b.width = w - (m.left+m.right);
42860             b.x = m.left;
42861             b.y = m.top;
42862             centerY = b.height + b.y + m.bottom;
42863             centerH -= centerY;
42864             north.updateBox(this.safeBox(b));
42865         }
42866         if(south && south.isVisible()){
42867             var b = south.getBox();
42868             var m = south.getMargins();
42869             b.width = w - (m.left+m.right);
42870             b.x = m.left;
42871             var totalHeight = (b.height + m.top + m.bottom);
42872             b.y = h - totalHeight + m.top;
42873             centerH -= totalHeight;
42874             south.updateBox(this.safeBox(b));
42875         }
42876         if(west && west.isVisible()){
42877             var b = west.getBox();
42878             var m = west.getMargins();
42879             b.height = centerH - (m.top+m.bottom);
42880             b.x = m.left;
42881             b.y = centerY + m.top;
42882             var totalWidth = (b.width + m.left + m.right);
42883             centerX += totalWidth;
42884             centerW -= totalWidth;
42885             west.updateBox(this.safeBox(b));
42886         }
42887         if(east && east.isVisible()){
42888             var b = east.getBox();
42889             var m = east.getMargins();
42890             b.height = centerH - (m.top+m.bottom);
42891             var totalWidth = (b.width + m.left + m.right);
42892             b.x = w - totalWidth + m.left;
42893             b.y = centerY + m.top;
42894             centerW -= totalWidth;
42895             east.updateBox(this.safeBox(b));
42896         }
42897         if(center){
42898             var m = center.getMargins();
42899             var centerBox = {
42900                 x: centerX + m.left,
42901                 y: centerY + m.top,
42902                 width: centerW - (m.left+m.right),
42903                 height: centerH - (m.top+m.bottom)
42904             };
42905             //if(this.hideOnLayout){
42906                 //center.el.setStyle("display", "block");
42907             //}
42908             center.updateBox(this.safeBox(centerBox));
42909         }
42910         this.el.repaint();
42911         this.fireEvent("layout", this);
42912     },
42913
42914     // private
42915     safeBox : function(box){
42916         box.width = Math.max(0, box.width);
42917         box.height = Math.max(0, box.height);
42918         return box;
42919     },
42920
42921     /**
42922      * Adds a ContentPanel (or subclass) to this layout.
42923      * @param {String} target The target region key (north, south, east, west or center).
42924      * @param {Roo.ContentPanel} panel The panel to add
42925      * @return {Roo.ContentPanel} The added panel
42926      */
42927     add : function(target, panel){
42928          
42929         target = target.toLowerCase();
42930         return this.regions[target].add(panel);
42931     },
42932
42933     /**
42934      * Remove a ContentPanel (or subclass) to this layout.
42935      * @param {String} target The target region key (north, south, east, west or center).
42936      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42937      * @return {Roo.ContentPanel} The removed panel
42938      */
42939     remove : function(target, panel){
42940         target = target.toLowerCase();
42941         return this.regions[target].remove(panel);
42942     },
42943
42944     /**
42945      * Searches all regions for a panel with the specified id
42946      * @param {String} panelId
42947      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42948      */
42949     findPanel : function(panelId){
42950         var rs = this.regions;
42951         for(var target in rs){
42952             if(typeof rs[target] != "function"){
42953                 var p = rs[target].getPanel(panelId);
42954                 if(p){
42955                     return p;
42956                 }
42957             }
42958         }
42959         return null;
42960     },
42961
42962     /**
42963      * Searches all regions for a panel with the specified id and activates (shows) it.
42964      * @param {String/ContentPanel} panelId The panels id or the panel itself
42965      * @return {Roo.ContentPanel} The shown panel or null
42966      */
42967     showPanel : function(panelId) {
42968       var rs = this.regions;
42969       for(var target in rs){
42970          var r = rs[target];
42971          if(typeof r != "function"){
42972             if(r.hasPanel(panelId)){
42973                return r.showPanel(panelId);
42974             }
42975          }
42976       }
42977       return null;
42978    },
42979
42980    /**
42981      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42982      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42983      */
42984    /*
42985     restoreState : function(provider){
42986         if(!provider){
42987             provider = Roo.state.Manager;
42988         }
42989         var sm = new Roo.LayoutStateManager();
42990         sm.init(this, provider);
42991     },
42992 */
42993  
42994  
42995     /**
42996      * Adds a xtype elements to the layout.
42997      * <pre><code>
42998
42999 layout.addxtype({
43000        xtype : 'ContentPanel',
43001        region: 'west',
43002        items: [ .... ]
43003    }
43004 );
43005
43006 layout.addxtype({
43007         xtype : 'NestedLayoutPanel',
43008         region: 'west',
43009         layout: {
43010            center: { },
43011            west: { }   
43012         },
43013         items : [ ... list of content panels or nested layout panels.. ]
43014    }
43015 );
43016 </code></pre>
43017      * @param {Object} cfg Xtype definition of item to add.
43018      */
43019     addxtype : function(cfg)
43020     {
43021         // basically accepts a pannel...
43022         // can accept a layout region..!?!?
43023         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43024         
43025         
43026         // theory?  children can only be panels??
43027         
43028         //if (!cfg.xtype.match(/Panel$/)) {
43029         //    return false;
43030         //}
43031         var ret = false;
43032         
43033         if (typeof(cfg.region) == 'undefined') {
43034             Roo.log("Failed to add Panel, region was not set");
43035             Roo.log(cfg);
43036             return false;
43037         }
43038         var region = cfg.region;
43039         delete cfg.region;
43040         
43041           
43042         var xitems = [];
43043         if (cfg.items) {
43044             xitems = cfg.items;
43045             delete cfg.items;
43046         }
43047         var nb = false;
43048         
43049         if ( region == 'center') {
43050             Roo.log("Center: " + cfg.title);
43051         }
43052         
43053         
43054         switch(cfg.xtype) 
43055         {
43056             case 'Content':  // ContentPanel (el, cfg)
43057             case 'Scroll':  // ContentPanel (el, cfg)
43058             case 'View': 
43059                 cfg.autoCreate = cfg.autoCreate || true;
43060                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43061                 //} else {
43062                 //    var el = this.el.createChild();
43063                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43064                 //}
43065                 
43066                 this.add(region, ret);
43067                 break;
43068             
43069             /*
43070             case 'TreePanel': // our new panel!
43071                 cfg.el = this.el.createChild();
43072                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43073                 this.add(region, ret);
43074                 break;
43075             */
43076             
43077             case 'Nest': 
43078                 // create a new Layout (which is  a Border Layout...
43079                 
43080                 var clayout = cfg.layout;
43081                 clayout.el  = this.el.createChild();
43082                 clayout.items   = clayout.items  || [];
43083                 
43084                 delete cfg.layout;
43085                 
43086                 // replace this exitems with the clayout ones..
43087                 xitems = clayout.items;
43088                  
43089                 // force background off if it's in center...
43090                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43091                     cfg.background = false;
43092                 }
43093                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
43094                 
43095                 
43096                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43097                 //console.log('adding nested layout panel '  + cfg.toSource());
43098                 this.add(region, ret);
43099                 nb = {}; /// find first...
43100                 break;
43101             
43102             case 'Grid':
43103                 
43104                 // needs grid and region
43105                 
43106                 //var el = this.getRegion(region).el.createChild();
43107                 /*
43108                  *var el = this.el.createChild();
43109                 // create the grid first...
43110                 cfg.grid.container = el;
43111                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43112                 */
43113                 
43114                 if (region == 'center' && this.active ) {
43115                     cfg.background = false;
43116                 }
43117                 
43118                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43119                 
43120                 this.add(region, ret);
43121                 /*
43122                 if (cfg.background) {
43123                     // render grid on panel activation (if panel background)
43124                     ret.on('activate', function(gp) {
43125                         if (!gp.grid.rendered) {
43126                     //        gp.grid.render(el);
43127                         }
43128                     });
43129                 } else {
43130                   //  cfg.grid.render(el);
43131                 }
43132                 */
43133                 break;
43134            
43135            
43136             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43137                 // it was the old xcomponent building that caused this before.
43138                 // espeically if border is the top element in the tree.
43139                 ret = this;
43140                 break; 
43141                 
43142                     
43143                 
43144                 
43145                 
43146             default:
43147                 /*
43148                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43149                     
43150                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43151                     this.add(region, ret);
43152                 } else {
43153                 */
43154                     Roo.log(cfg);
43155                     throw "Can not add '" + cfg.xtype + "' to Border";
43156                     return null;
43157              
43158                                 
43159              
43160         }
43161         this.beginUpdate();
43162         // add children..
43163         var region = '';
43164         var abn = {};
43165         Roo.each(xitems, function(i)  {
43166             region = nb && i.region ? i.region : false;
43167             
43168             var add = ret.addxtype(i);
43169            
43170             if (region) {
43171                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43172                 if (!i.background) {
43173                     abn[region] = nb[region] ;
43174                 }
43175             }
43176             
43177         });
43178         this.endUpdate();
43179
43180         // make the last non-background panel active..
43181         //if (nb) { Roo.log(abn); }
43182         if (nb) {
43183             
43184             for(var r in abn) {
43185                 region = this.getRegion(r);
43186                 if (region) {
43187                     // tried using nb[r], but it does not work..
43188                      
43189                     region.showPanel(abn[r]);
43190                    
43191                 }
43192             }
43193         }
43194         return ret;
43195         
43196     },
43197     
43198     
43199 // private
43200     factory : function(cfg)
43201     {
43202         
43203         var validRegions = Roo.bootstrap.layout.Border.regions;
43204
43205         var target = cfg.region;
43206         cfg.mgr = this;
43207         
43208         var r = Roo.bootstrap.layout;
43209         Roo.log(target);
43210         switch(target){
43211             case "north":
43212                 return new r.North(cfg);
43213             case "south":
43214                 return new r.South(cfg);
43215             case "east":
43216                 return new r.East(cfg);
43217             case "west":
43218                 return new r.West(cfg);
43219             case "center":
43220                 return new r.Center(cfg);
43221         }
43222         throw 'Layout region "'+target+'" not supported.';
43223     }
43224     
43225     
43226 });
43227  /*
43228  * Based on:
43229  * Ext JS Library 1.1.1
43230  * Copyright(c) 2006-2007, Ext JS, LLC.
43231  *
43232  * Originally Released Under LGPL - original licence link has changed is not relivant.
43233  *
43234  * Fork - LGPL
43235  * <script type="text/javascript">
43236  */
43237  
43238 /**
43239  * @class Roo.bootstrap.layout.Basic
43240  * @extends Roo.util.Observable
43241  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43242  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43243  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43244  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43245  * @cfg {string}   region  the region that it inhabits..
43246  * @cfg {bool}   skipConfig skip config?
43247  * 
43248
43249  */
43250 Roo.bootstrap.layout.Basic = function(config){
43251     
43252     this.mgr = config.mgr;
43253     
43254     this.position = config.region;
43255     
43256     var skipConfig = config.skipConfig;
43257     
43258     this.events = {
43259         /**
43260          * @scope Roo.BasicLayoutRegion
43261          */
43262         
43263         /**
43264          * @event beforeremove
43265          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43266          * @param {Roo.LayoutRegion} this
43267          * @param {Roo.ContentPanel} panel The panel
43268          * @param {Object} e The cancel event object
43269          */
43270         "beforeremove" : true,
43271         /**
43272          * @event invalidated
43273          * Fires when the layout for this region is changed.
43274          * @param {Roo.LayoutRegion} this
43275          */
43276         "invalidated" : true,
43277         /**
43278          * @event visibilitychange
43279          * Fires when this region is shown or hidden 
43280          * @param {Roo.LayoutRegion} this
43281          * @param {Boolean} visibility true or false
43282          */
43283         "visibilitychange" : true,
43284         /**
43285          * @event paneladded
43286          * Fires when a panel is added. 
43287          * @param {Roo.LayoutRegion} this
43288          * @param {Roo.ContentPanel} panel The panel
43289          */
43290         "paneladded" : true,
43291         /**
43292          * @event panelremoved
43293          * Fires when a panel is removed. 
43294          * @param {Roo.LayoutRegion} this
43295          * @param {Roo.ContentPanel} panel The panel
43296          */
43297         "panelremoved" : true,
43298         /**
43299          * @event beforecollapse
43300          * Fires when this region before collapse.
43301          * @param {Roo.LayoutRegion} this
43302          */
43303         "beforecollapse" : true,
43304         /**
43305          * @event collapsed
43306          * Fires when this region is collapsed.
43307          * @param {Roo.LayoutRegion} this
43308          */
43309         "collapsed" : true,
43310         /**
43311          * @event expanded
43312          * Fires when this region is expanded.
43313          * @param {Roo.LayoutRegion} this
43314          */
43315         "expanded" : true,
43316         /**
43317          * @event slideshow
43318          * Fires when this region is slid into view.
43319          * @param {Roo.LayoutRegion} this
43320          */
43321         "slideshow" : true,
43322         /**
43323          * @event slidehide
43324          * Fires when this region slides out of view. 
43325          * @param {Roo.LayoutRegion} this
43326          */
43327         "slidehide" : true,
43328         /**
43329          * @event panelactivated
43330          * Fires when a panel is activated. 
43331          * @param {Roo.LayoutRegion} this
43332          * @param {Roo.ContentPanel} panel The activated panel
43333          */
43334         "panelactivated" : true,
43335         /**
43336          * @event resized
43337          * Fires when the user resizes this region. 
43338          * @param {Roo.LayoutRegion} this
43339          * @param {Number} newSize The new size (width for east/west, height for north/south)
43340          */
43341         "resized" : true
43342     };
43343     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43344     this.panels = new Roo.util.MixedCollection();
43345     this.panels.getKey = this.getPanelId.createDelegate(this);
43346     this.box = null;
43347     this.activePanel = null;
43348     // ensure listeners are added...
43349     
43350     if (config.listeners || config.events) {
43351         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43352             listeners : config.listeners || {},
43353             events : config.events || {}
43354         });
43355     }
43356     
43357     if(skipConfig !== true){
43358         this.applyConfig(config);
43359     }
43360 };
43361
43362 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43363 {
43364     getPanelId : function(p){
43365         return p.getId();
43366     },
43367     
43368     applyConfig : function(config){
43369         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43370         this.config = config;
43371         
43372     },
43373     
43374     /**
43375      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43376      * the width, for horizontal (north, south) the height.
43377      * @param {Number} newSize The new width or height
43378      */
43379     resizeTo : function(newSize){
43380         var el = this.el ? this.el :
43381                  (this.activePanel ? this.activePanel.getEl() : null);
43382         if(el){
43383             switch(this.position){
43384                 case "east":
43385                 case "west":
43386                     el.setWidth(newSize);
43387                     this.fireEvent("resized", this, newSize);
43388                 break;
43389                 case "north":
43390                 case "south":
43391                     el.setHeight(newSize);
43392                     this.fireEvent("resized", this, newSize);
43393                 break;                
43394             }
43395         }
43396     },
43397     
43398     getBox : function(){
43399         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43400     },
43401     
43402     getMargins : function(){
43403         return this.margins;
43404     },
43405     
43406     updateBox : function(box){
43407         this.box = box;
43408         var el = this.activePanel.getEl();
43409         el.dom.style.left = box.x + "px";
43410         el.dom.style.top = box.y + "px";
43411         this.activePanel.setSize(box.width, box.height);
43412     },
43413     
43414     /**
43415      * Returns the container element for this region.
43416      * @return {Roo.Element}
43417      */
43418     getEl : function(){
43419         return this.activePanel;
43420     },
43421     
43422     /**
43423      * Returns true if this region is currently visible.
43424      * @return {Boolean}
43425      */
43426     isVisible : function(){
43427         return this.activePanel ? true : false;
43428     },
43429     
43430     setActivePanel : function(panel){
43431         panel = this.getPanel(panel);
43432         if(this.activePanel && this.activePanel != panel){
43433             this.activePanel.setActiveState(false);
43434             this.activePanel.getEl().setLeftTop(-10000,-10000);
43435         }
43436         this.activePanel = panel;
43437         panel.setActiveState(true);
43438         if(this.box){
43439             panel.setSize(this.box.width, this.box.height);
43440         }
43441         this.fireEvent("panelactivated", this, panel);
43442         this.fireEvent("invalidated");
43443     },
43444     
43445     /**
43446      * Show the specified panel.
43447      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43448      * @return {Roo.ContentPanel} The shown panel or null
43449      */
43450     showPanel : function(panel){
43451         panel = this.getPanel(panel);
43452         if(panel){
43453             this.setActivePanel(panel);
43454         }
43455         return panel;
43456     },
43457     
43458     /**
43459      * Get the active panel for this region.
43460      * @return {Roo.ContentPanel} The active panel or null
43461      */
43462     getActivePanel : function(){
43463         return this.activePanel;
43464     },
43465     
43466     /**
43467      * Add the passed ContentPanel(s)
43468      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43469      * @return {Roo.ContentPanel} The panel added (if only one was added)
43470      */
43471     add : function(panel){
43472         if(arguments.length > 1){
43473             for(var i = 0, len = arguments.length; i < len; i++) {
43474                 this.add(arguments[i]);
43475             }
43476             return null;
43477         }
43478         if(this.hasPanel(panel)){
43479             this.showPanel(panel);
43480             return panel;
43481         }
43482         var el = panel.getEl();
43483         if(el.dom.parentNode != this.mgr.el.dom){
43484             this.mgr.el.dom.appendChild(el.dom);
43485         }
43486         if(panel.setRegion){
43487             panel.setRegion(this);
43488         }
43489         this.panels.add(panel);
43490         el.setStyle("position", "absolute");
43491         if(!panel.background){
43492             this.setActivePanel(panel);
43493             if(this.config.initialSize && this.panels.getCount()==1){
43494                 this.resizeTo(this.config.initialSize);
43495             }
43496         }
43497         this.fireEvent("paneladded", this, panel);
43498         return panel;
43499     },
43500     
43501     /**
43502      * Returns true if the panel is in this region.
43503      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43504      * @return {Boolean}
43505      */
43506     hasPanel : function(panel){
43507         if(typeof panel == "object"){ // must be panel obj
43508             panel = panel.getId();
43509         }
43510         return this.getPanel(panel) ? true : false;
43511     },
43512     
43513     /**
43514      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43515      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43516      * @param {Boolean} preservePanel Overrides the config preservePanel option
43517      * @return {Roo.ContentPanel} The panel that was removed
43518      */
43519     remove : function(panel, preservePanel){
43520         panel = this.getPanel(panel);
43521         if(!panel){
43522             return null;
43523         }
43524         var e = {};
43525         this.fireEvent("beforeremove", this, panel, e);
43526         if(e.cancel === true){
43527             return null;
43528         }
43529         var panelId = panel.getId();
43530         this.panels.removeKey(panelId);
43531         return panel;
43532     },
43533     
43534     /**
43535      * Returns the panel specified or null if it's not in this region.
43536      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43537      * @return {Roo.ContentPanel}
43538      */
43539     getPanel : function(id){
43540         if(typeof id == "object"){ // must be panel obj
43541             return id;
43542         }
43543         return this.panels.get(id);
43544     },
43545     
43546     /**
43547      * Returns this regions position (north/south/east/west/center).
43548      * @return {String} 
43549      */
43550     getPosition: function(){
43551         return this.position;    
43552     }
43553 });/*
43554  * Based on:
43555  * Ext JS Library 1.1.1
43556  * Copyright(c) 2006-2007, Ext JS, LLC.
43557  *
43558  * Originally Released Under LGPL - original licence link has changed is not relivant.
43559  *
43560  * Fork - LGPL
43561  * <script type="text/javascript">
43562  */
43563  
43564 /**
43565  * @class Roo.bootstrap.layout.Region
43566  * @extends Roo.bootstrap.layout.Basic
43567  * This class represents a region in a layout manager.
43568  
43569  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43570  * @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})
43571  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43572  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43573  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43574  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43575  * @cfg {String}    title           The title for the region (overrides panel titles)
43576  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43577  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43578  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43579  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43580  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43581  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43582  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43583  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43584  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43585  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43586
43587  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43588  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43589  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43590  * @cfg {Number}    width           For East/West panels
43591  * @cfg {Number}    height          For North/South panels
43592  * @cfg {Boolean}   split           To show the splitter
43593  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43594  * 
43595  * @cfg {string}   cls             Extra CSS classes to add to region
43596  * 
43597  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43598  * @cfg {string}   region  the region that it inhabits..
43599  *
43600
43601  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43602  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43603
43604  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43605  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43606  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43607  */
43608 Roo.bootstrap.layout.Region = function(config)
43609 {
43610     this.applyConfig(config);
43611
43612     var mgr = config.mgr;
43613     var pos = config.region;
43614     config.skipConfig = true;
43615     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43616     
43617     if (mgr.el) {
43618         this.onRender(mgr.el);   
43619     }
43620      
43621     this.visible = true;
43622     this.collapsed = false;
43623     this.unrendered_panels = [];
43624 };
43625
43626 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43627
43628     position: '', // set by wrapper (eg. north/south etc..)
43629     unrendered_panels : null,  // unrendered panels.
43630     
43631     tabPosition : false,
43632     
43633     mgr: false, // points to 'Border'
43634     
43635     
43636     createBody : function(){
43637         /** This region's body element 
43638         * @type Roo.Element */
43639         this.bodyEl = this.el.createChild({
43640                 tag: "div",
43641                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43642         });
43643     },
43644
43645     onRender: function(ctr, pos)
43646     {
43647         var dh = Roo.DomHelper;
43648         /** This region's container element 
43649         * @type Roo.Element */
43650         this.el = dh.append(ctr.dom, {
43651                 tag: "div",
43652                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43653             }, true);
43654         /** This region's title element 
43655         * @type Roo.Element */
43656     
43657         this.titleEl = dh.append(this.el.dom,  {
43658                 tag: "div",
43659                 unselectable: "on",
43660                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43661                 children:[
43662                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43663                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43664                 ]
43665             }, true);
43666         
43667         this.titleEl.enableDisplayMode();
43668         /** This region's title text element 
43669         * @type HTMLElement */
43670         this.titleTextEl = this.titleEl.dom.firstChild;
43671         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43672         /*
43673         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43674         this.closeBtn.enableDisplayMode();
43675         this.closeBtn.on("click", this.closeClicked, this);
43676         this.closeBtn.hide();
43677     */
43678         this.createBody(this.config);
43679         if(this.config.hideWhenEmpty){
43680             this.hide();
43681             this.on("paneladded", this.validateVisibility, this);
43682             this.on("panelremoved", this.validateVisibility, this);
43683         }
43684         if(this.autoScroll){
43685             this.bodyEl.setStyle("overflow", "auto");
43686         }else{
43687             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43688         }
43689         //if(c.titlebar !== false){
43690             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43691                 this.titleEl.hide();
43692             }else{
43693                 this.titleEl.show();
43694                 if(this.config.title){
43695                     this.titleTextEl.innerHTML = this.config.title;
43696                 }
43697             }
43698         //}
43699         if(this.config.collapsed){
43700             this.collapse(true);
43701         }
43702         if(this.config.hidden){
43703             this.hide();
43704         }
43705         
43706         if (this.unrendered_panels && this.unrendered_panels.length) {
43707             for (var i =0;i< this.unrendered_panels.length; i++) {
43708                 this.add(this.unrendered_panels[i]);
43709             }
43710             this.unrendered_panels = null;
43711             
43712         }
43713         
43714     },
43715     
43716     applyConfig : function(c)
43717     {
43718         /*
43719          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43720             var dh = Roo.DomHelper;
43721             if(c.titlebar !== false){
43722                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43723                 this.collapseBtn.on("click", this.collapse, this);
43724                 this.collapseBtn.enableDisplayMode();
43725                 /*
43726                 if(c.showPin === true || this.showPin){
43727                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43728                     this.stickBtn.enableDisplayMode();
43729                     this.stickBtn.on("click", this.expand, this);
43730                     this.stickBtn.hide();
43731                 }
43732                 
43733             }
43734             */
43735             /** This region's collapsed element
43736             * @type Roo.Element */
43737             /*
43738              *
43739             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43740                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43741             ]}, true);
43742             
43743             if(c.floatable !== false){
43744                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43745                this.collapsedEl.on("click", this.collapseClick, this);
43746             }
43747
43748             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43749                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43750                    id: "message", unselectable: "on", style:{"float":"left"}});
43751                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43752              }
43753             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43754             this.expandBtn.on("click", this.expand, this);
43755             
43756         }
43757         
43758         if(this.collapseBtn){
43759             this.collapseBtn.setVisible(c.collapsible == true);
43760         }
43761         
43762         this.cmargins = c.cmargins || this.cmargins ||
43763                          (this.position == "west" || this.position == "east" ?
43764                              {top: 0, left: 2, right:2, bottom: 0} :
43765                              {top: 2, left: 0, right:0, bottom: 2});
43766         */
43767         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43768         
43769         
43770         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43771         
43772         this.autoScroll = c.autoScroll || false;
43773         
43774         
43775        
43776         
43777         this.duration = c.duration || .30;
43778         this.slideDuration = c.slideDuration || .45;
43779         this.config = c;
43780        
43781     },
43782     /**
43783      * Returns true if this region is currently visible.
43784      * @return {Boolean}
43785      */
43786     isVisible : function(){
43787         return this.visible;
43788     },
43789
43790     /**
43791      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43792      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43793      */
43794     //setCollapsedTitle : function(title){
43795     //    title = title || "&#160;";
43796      //   if(this.collapsedTitleTextEl){
43797       //      this.collapsedTitleTextEl.innerHTML = title;
43798        // }
43799     //},
43800
43801     getBox : function(){
43802         var b;
43803       //  if(!this.collapsed){
43804             b = this.el.getBox(false, true);
43805        // }else{
43806           //  b = this.collapsedEl.getBox(false, true);
43807         //}
43808         return b;
43809     },
43810
43811     getMargins : function(){
43812         return this.margins;
43813         //return this.collapsed ? this.cmargins : this.margins;
43814     },
43815 /*
43816     highlight : function(){
43817         this.el.addClass("x-layout-panel-dragover");
43818     },
43819
43820     unhighlight : function(){
43821         this.el.removeClass("x-layout-panel-dragover");
43822     },
43823 */
43824     updateBox : function(box)
43825     {
43826         if (!this.bodyEl) {
43827             return; // not rendered yet..
43828         }
43829         
43830         this.box = box;
43831         if(!this.collapsed){
43832             this.el.dom.style.left = box.x + "px";
43833             this.el.dom.style.top = box.y + "px";
43834             this.updateBody(box.width, box.height);
43835         }else{
43836             this.collapsedEl.dom.style.left = box.x + "px";
43837             this.collapsedEl.dom.style.top = box.y + "px";
43838             this.collapsedEl.setSize(box.width, box.height);
43839         }
43840         if(this.tabs){
43841             this.tabs.autoSizeTabs();
43842         }
43843     },
43844
43845     updateBody : function(w, h)
43846     {
43847         if(w !== null){
43848             this.el.setWidth(w);
43849             w -= this.el.getBorderWidth("rl");
43850             if(this.config.adjustments){
43851                 w += this.config.adjustments[0];
43852             }
43853         }
43854         if(h !== null && h > 0){
43855             this.el.setHeight(h);
43856             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43857             h -= this.el.getBorderWidth("tb");
43858             if(this.config.adjustments){
43859                 h += this.config.adjustments[1];
43860             }
43861             this.bodyEl.setHeight(h);
43862             if(this.tabs){
43863                 h = this.tabs.syncHeight(h);
43864             }
43865         }
43866         if(this.panelSize){
43867             w = w !== null ? w : this.panelSize.width;
43868             h = h !== null ? h : this.panelSize.height;
43869         }
43870         if(this.activePanel){
43871             var el = this.activePanel.getEl();
43872             w = w !== null ? w : el.getWidth();
43873             h = h !== null ? h : el.getHeight();
43874             this.panelSize = {width: w, height: h};
43875             this.activePanel.setSize(w, h);
43876         }
43877         if(Roo.isIE && this.tabs){
43878             this.tabs.el.repaint();
43879         }
43880     },
43881
43882     /**
43883      * Returns the container element for this region.
43884      * @return {Roo.Element}
43885      */
43886     getEl : function(){
43887         return this.el;
43888     },
43889
43890     /**
43891      * Hides this region.
43892      */
43893     hide : function(){
43894         //if(!this.collapsed){
43895             this.el.dom.style.left = "-2000px";
43896             this.el.hide();
43897         //}else{
43898          //   this.collapsedEl.dom.style.left = "-2000px";
43899          //   this.collapsedEl.hide();
43900        // }
43901         this.visible = false;
43902         this.fireEvent("visibilitychange", this, false);
43903     },
43904
43905     /**
43906      * Shows this region if it was previously hidden.
43907      */
43908     show : function(){
43909         //if(!this.collapsed){
43910             this.el.show();
43911         //}else{
43912         //    this.collapsedEl.show();
43913        // }
43914         this.visible = true;
43915         this.fireEvent("visibilitychange", this, true);
43916     },
43917 /*
43918     closeClicked : function(){
43919         if(this.activePanel){
43920             this.remove(this.activePanel);
43921         }
43922     },
43923
43924     collapseClick : function(e){
43925         if(this.isSlid){
43926            e.stopPropagation();
43927            this.slideIn();
43928         }else{
43929            e.stopPropagation();
43930            this.slideOut();
43931         }
43932     },
43933 */
43934     /**
43935      * Collapses this region.
43936      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43937      */
43938     /*
43939     collapse : function(skipAnim, skipCheck = false){
43940         if(this.collapsed) {
43941             return;
43942         }
43943         
43944         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43945             
43946             this.collapsed = true;
43947             if(this.split){
43948                 this.split.el.hide();
43949             }
43950             if(this.config.animate && skipAnim !== true){
43951                 this.fireEvent("invalidated", this);
43952                 this.animateCollapse();
43953             }else{
43954                 this.el.setLocation(-20000,-20000);
43955                 this.el.hide();
43956                 this.collapsedEl.show();
43957                 this.fireEvent("collapsed", this);
43958                 this.fireEvent("invalidated", this);
43959             }
43960         }
43961         
43962     },
43963 */
43964     animateCollapse : function(){
43965         // overridden
43966     },
43967
43968     /**
43969      * Expands this region if it was previously collapsed.
43970      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43971      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43972      */
43973     /*
43974     expand : function(e, skipAnim){
43975         if(e) {
43976             e.stopPropagation();
43977         }
43978         if(!this.collapsed || this.el.hasActiveFx()) {
43979             return;
43980         }
43981         if(this.isSlid){
43982             this.afterSlideIn();
43983             skipAnim = true;
43984         }
43985         this.collapsed = false;
43986         if(this.config.animate && skipAnim !== true){
43987             this.animateExpand();
43988         }else{
43989             this.el.show();
43990             if(this.split){
43991                 this.split.el.show();
43992             }
43993             this.collapsedEl.setLocation(-2000,-2000);
43994             this.collapsedEl.hide();
43995             this.fireEvent("invalidated", this);
43996             this.fireEvent("expanded", this);
43997         }
43998     },
43999 */
44000     animateExpand : function(){
44001         // overridden
44002     },
44003
44004     initTabs : function()
44005     {
44006         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
44007         
44008         var ts = new Roo.bootstrap.panel.Tabs({
44009             el: this.bodyEl.dom,
44010             region : this,
44011             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
44012             disableTooltips: this.config.disableTabTips,
44013             toolbar : this.config.toolbar
44014         });
44015         
44016         if(this.config.hideTabs){
44017             ts.stripWrap.setDisplayed(false);
44018         }
44019         this.tabs = ts;
44020         ts.resizeTabs = this.config.resizeTabs === true;
44021         ts.minTabWidth = this.config.minTabWidth || 40;
44022         ts.maxTabWidth = this.config.maxTabWidth || 250;
44023         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44024         ts.monitorResize = false;
44025         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44026         ts.bodyEl.addClass('roo-layout-tabs-body');
44027         this.panels.each(this.initPanelAsTab, this);
44028     },
44029
44030     initPanelAsTab : function(panel){
44031         var ti = this.tabs.addTab(
44032             panel.getEl().id,
44033             panel.getTitle(),
44034             null,
44035             this.config.closeOnTab && panel.isClosable(),
44036             panel.tpl
44037         );
44038         if(panel.tabTip !== undefined){
44039             ti.setTooltip(panel.tabTip);
44040         }
44041         ti.on("activate", function(){
44042               this.setActivePanel(panel);
44043         }, this);
44044         
44045         if(this.config.closeOnTab){
44046             ti.on("beforeclose", function(t, e){
44047                 e.cancel = true;
44048                 this.remove(panel);
44049             }, this);
44050         }
44051         
44052         panel.tabItem = ti;
44053         
44054         return ti;
44055     },
44056
44057     updatePanelTitle : function(panel, title)
44058     {
44059         if(this.activePanel == panel){
44060             this.updateTitle(title);
44061         }
44062         if(this.tabs){
44063             var ti = this.tabs.getTab(panel.getEl().id);
44064             ti.setText(title);
44065             if(panel.tabTip !== undefined){
44066                 ti.setTooltip(panel.tabTip);
44067             }
44068         }
44069     },
44070
44071     updateTitle : function(title){
44072         if(this.titleTextEl && !this.config.title){
44073             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44074         }
44075     },
44076
44077     setActivePanel : function(panel)
44078     {
44079         panel = this.getPanel(panel);
44080         if(this.activePanel && this.activePanel != panel){
44081             if(this.activePanel.setActiveState(false) === false){
44082                 return;
44083             }
44084         }
44085         this.activePanel = panel;
44086         panel.setActiveState(true);
44087         if(this.panelSize){
44088             panel.setSize(this.panelSize.width, this.panelSize.height);
44089         }
44090         if(this.closeBtn){
44091             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44092         }
44093         this.updateTitle(panel.getTitle());
44094         if(this.tabs){
44095             this.fireEvent("invalidated", this);
44096         }
44097         this.fireEvent("panelactivated", this, panel);
44098     },
44099
44100     /**
44101      * Shows the specified panel.
44102      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44103      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44104      */
44105     showPanel : function(panel)
44106     {
44107         panel = this.getPanel(panel);
44108         if(panel){
44109             if(this.tabs){
44110                 var tab = this.tabs.getTab(panel.getEl().id);
44111                 if(tab.isHidden()){
44112                     this.tabs.unhideTab(tab.id);
44113                 }
44114                 tab.activate();
44115             }else{
44116                 this.setActivePanel(panel);
44117             }
44118         }
44119         return panel;
44120     },
44121
44122     /**
44123      * Get the active panel for this region.
44124      * @return {Roo.ContentPanel} The active panel or null
44125      */
44126     getActivePanel : function(){
44127         return this.activePanel;
44128     },
44129
44130     validateVisibility : function(){
44131         if(this.panels.getCount() < 1){
44132             this.updateTitle("&#160;");
44133             this.closeBtn.hide();
44134             this.hide();
44135         }else{
44136             if(!this.isVisible()){
44137                 this.show();
44138             }
44139         }
44140     },
44141
44142     /**
44143      * Adds the passed ContentPanel(s) to this region.
44144      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44145      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44146      */
44147     add : function(panel)
44148     {
44149         if(arguments.length > 1){
44150             for(var i = 0, len = arguments.length; i < len; i++) {
44151                 this.add(arguments[i]);
44152             }
44153             return null;
44154         }
44155         
44156         // if we have not been rendered yet, then we can not really do much of this..
44157         if (!this.bodyEl) {
44158             this.unrendered_panels.push(panel);
44159             return panel;
44160         }
44161         
44162         
44163         
44164         
44165         if(this.hasPanel(panel)){
44166             this.showPanel(panel);
44167             return panel;
44168         }
44169         panel.setRegion(this);
44170         this.panels.add(panel);
44171        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44172             // sinle panel - no tab...?? would it not be better to render it with the tabs,
44173             // and hide them... ???
44174             this.bodyEl.dom.appendChild(panel.getEl().dom);
44175             if(panel.background !== true){
44176                 this.setActivePanel(panel);
44177             }
44178             this.fireEvent("paneladded", this, panel);
44179             return panel;
44180         }
44181         */
44182         if(!this.tabs){
44183             this.initTabs();
44184         }else{
44185             this.initPanelAsTab(panel);
44186         }
44187         
44188         
44189         if(panel.background !== true){
44190             this.tabs.activate(panel.getEl().id);
44191         }
44192         this.fireEvent("paneladded", this, panel);
44193         return panel;
44194     },
44195
44196     /**
44197      * Hides the tab for the specified panel.
44198      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44199      */
44200     hidePanel : function(panel){
44201         if(this.tabs && (panel = this.getPanel(panel))){
44202             this.tabs.hideTab(panel.getEl().id);
44203         }
44204     },
44205
44206     /**
44207      * Unhides the tab for a previously hidden panel.
44208      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44209      */
44210     unhidePanel : function(panel){
44211         if(this.tabs && (panel = this.getPanel(panel))){
44212             this.tabs.unhideTab(panel.getEl().id);
44213         }
44214     },
44215
44216     clearPanels : function(){
44217         while(this.panels.getCount() > 0){
44218              this.remove(this.panels.first());
44219         }
44220     },
44221
44222     /**
44223      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44224      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44225      * @param {Boolean} preservePanel Overrides the config preservePanel option
44226      * @return {Roo.ContentPanel} The panel that was removed
44227      */
44228     remove : function(panel, preservePanel)
44229     {
44230         panel = this.getPanel(panel);
44231         if(!panel){
44232             return null;
44233         }
44234         var e = {};
44235         this.fireEvent("beforeremove", this, panel, e);
44236         if(e.cancel === true){
44237             return null;
44238         }
44239         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44240         var panelId = panel.getId();
44241         this.panels.removeKey(panelId);
44242         if(preservePanel){
44243             document.body.appendChild(panel.getEl().dom);
44244         }
44245         if(this.tabs){
44246             this.tabs.removeTab(panel.getEl().id);
44247         }else if (!preservePanel){
44248             this.bodyEl.dom.removeChild(panel.getEl().dom);
44249         }
44250         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44251             var p = this.panels.first();
44252             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44253             tempEl.appendChild(p.getEl().dom);
44254             this.bodyEl.update("");
44255             this.bodyEl.dom.appendChild(p.getEl().dom);
44256             tempEl = null;
44257             this.updateTitle(p.getTitle());
44258             this.tabs = null;
44259             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44260             this.setActivePanel(p);
44261         }
44262         panel.setRegion(null);
44263         if(this.activePanel == panel){
44264             this.activePanel = null;
44265         }
44266         if(this.config.autoDestroy !== false && preservePanel !== true){
44267             try{panel.destroy();}catch(e){}
44268         }
44269         this.fireEvent("panelremoved", this, panel);
44270         return panel;
44271     },
44272
44273     /**
44274      * Returns the TabPanel component used by this region
44275      * @return {Roo.TabPanel}
44276      */
44277     getTabs : function(){
44278         return this.tabs;
44279     },
44280
44281     createTool : function(parentEl, className){
44282         var btn = Roo.DomHelper.append(parentEl, {
44283             tag: "div",
44284             cls: "x-layout-tools-button",
44285             children: [ {
44286                 tag: "div",
44287                 cls: "roo-layout-tools-button-inner " + className,
44288                 html: "&#160;"
44289             }]
44290         }, true);
44291         btn.addClassOnOver("roo-layout-tools-button-over");
44292         return btn;
44293     }
44294 });/*
44295  * Based on:
44296  * Ext JS Library 1.1.1
44297  * Copyright(c) 2006-2007, Ext JS, LLC.
44298  *
44299  * Originally Released Under LGPL - original licence link has changed is not relivant.
44300  *
44301  * Fork - LGPL
44302  * <script type="text/javascript">
44303  */
44304  
44305
44306
44307 /**
44308  * @class Roo.SplitLayoutRegion
44309  * @extends Roo.LayoutRegion
44310  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44311  */
44312 Roo.bootstrap.layout.Split = function(config){
44313     this.cursor = config.cursor;
44314     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44315 };
44316
44317 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44318 {
44319     splitTip : "Drag to resize.",
44320     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44321     useSplitTips : false,
44322
44323     applyConfig : function(config){
44324         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44325     },
44326     
44327     onRender : function(ctr,pos) {
44328         
44329         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44330         if(!this.config.split){
44331             return;
44332         }
44333         if(!this.split){
44334             
44335             var splitEl = Roo.DomHelper.append(ctr.dom,  {
44336                             tag: "div",
44337                             id: this.el.id + "-split",
44338                             cls: "roo-layout-split roo-layout-split-"+this.position,
44339                             html: "&#160;"
44340             });
44341             /** The SplitBar for this region 
44342             * @type Roo.SplitBar */
44343             // does not exist yet...
44344             Roo.log([this.position, this.orientation]);
44345             
44346             this.split = new Roo.bootstrap.SplitBar({
44347                 dragElement : splitEl,
44348                 resizingElement: this.el,
44349                 orientation : this.orientation
44350             });
44351             
44352             this.split.on("moved", this.onSplitMove, this);
44353             this.split.useShim = this.config.useShim === true;
44354             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44355             if(this.useSplitTips){
44356                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44357             }
44358             //if(config.collapsible){
44359             //    this.split.el.on("dblclick", this.collapse,  this);
44360             //}
44361         }
44362         if(typeof this.config.minSize != "undefined"){
44363             this.split.minSize = this.config.minSize;
44364         }
44365         if(typeof this.config.maxSize != "undefined"){
44366             this.split.maxSize = this.config.maxSize;
44367         }
44368         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44369             this.hideSplitter();
44370         }
44371         
44372     },
44373
44374     getHMaxSize : function(){
44375          var cmax = this.config.maxSize || 10000;
44376          var center = this.mgr.getRegion("center");
44377          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44378     },
44379
44380     getVMaxSize : function(){
44381          var cmax = this.config.maxSize || 10000;
44382          var center = this.mgr.getRegion("center");
44383          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44384     },
44385
44386     onSplitMove : function(split, newSize){
44387         this.fireEvent("resized", this, newSize);
44388     },
44389     
44390     /** 
44391      * Returns the {@link Roo.SplitBar} for this region.
44392      * @return {Roo.SplitBar}
44393      */
44394     getSplitBar : function(){
44395         return this.split;
44396     },
44397     
44398     hide : function(){
44399         this.hideSplitter();
44400         Roo.bootstrap.layout.Split.superclass.hide.call(this);
44401     },
44402
44403     hideSplitter : function(){
44404         if(this.split){
44405             this.split.el.setLocation(-2000,-2000);
44406             this.split.el.hide();
44407         }
44408     },
44409
44410     show : function(){
44411         if(this.split){
44412             this.split.el.show();
44413         }
44414         Roo.bootstrap.layout.Split.superclass.show.call(this);
44415     },
44416     
44417     beforeSlide: function(){
44418         if(Roo.isGecko){// firefox overflow auto bug workaround
44419             this.bodyEl.clip();
44420             if(this.tabs) {
44421                 this.tabs.bodyEl.clip();
44422             }
44423             if(this.activePanel){
44424                 this.activePanel.getEl().clip();
44425                 
44426                 if(this.activePanel.beforeSlide){
44427                     this.activePanel.beforeSlide();
44428                 }
44429             }
44430         }
44431     },
44432     
44433     afterSlide : function(){
44434         if(Roo.isGecko){// firefox overflow auto bug workaround
44435             this.bodyEl.unclip();
44436             if(this.tabs) {
44437                 this.tabs.bodyEl.unclip();
44438             }
44439             if(this.activePanel){
44440                 this.activePanel.getEl().unclip();
44441                 if(this.activePanel.afterSlide){
44442                     this.activePanel.afterSlide();
44443                 }
44444             }
44445         }
44446     },
44447
44448     initAutoHide : function(){
44449         if(this.autoHide !== false){
44450             if(!this.autoHideHd){
44451                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44452                 this.autoHideHd = {
44453                     "mouseout": function(e){
44454                         if(!e.within(this.el, true)){
44455                             st.delay(500);
44456                         }
44457                     },
44458                     "mouseover" : function(e){
44459                         st.cancel();
44460                     },
44461                     scope : this
44462                 };
44463             }
44464             this.el.on(this.autoHideHd);
44465         }
44466     },
44467
44468     clearAutoHide : function(){
44469         if(this.autoHide !== false){
44470             this.el.un("mouseout", this.autoHideHd.mouseout);
44471             this.el.un("mouseover", this.autoHideHd.mouseover);
44472         }
44473     },
44474
44475     clearMonitor : function(){
44476         Roo.get(document).un("click", this.slideInIf, this);
44477     },
44478
44479     // these names are backwards but not changed for compat
44480     slideOut : function(){
44481         if(this.isSlid || this.el.hasActiveFx()){
44482             return;
44483         }
44484         this.isSlid = true;
44485         if(this.collapseBtn){
44486             this.collapseBtn.hide();
44487         }
44488         this.closeBtnState = this.closeBtn.getStyle('display');
44489         this.closeBtn.hide();
44490         if(this.stickBtn){
44491             this.stickBtn.show();
44492         }
44493         this.el.show();
44494         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44495         this.beforeSlide();
44496         this.el.setStyle("z-index", 10001);
44497         this.el.slideIn(this.getSlideAnchor(), {
44498             callback: function(){
44499                 this.afterSlide();
44500                 this.initAutoHide();
44501                 Roo.get(document).on("click", this.slideInIf, this);
44502                 this.fireEvent("slideshow", this);
44503             },
44504             scope: this,
44505             block: true
44506         });
44507     },
44508
44509     afterSlideIn : function(){
44510         this.clearAutoHide();
44511         this.isSlid = false;
44512         this.clearMonitor();
44513         this.el.setStyle("z-index", "");
44514         if(this.collapseBtn){
44515             this.collapseBtn.show();
44516         }
44517         this.closeBtn.setStyle('display', this.closeBtnState);
44518         if(this.stickBtn){
44519             this.stickBtn.hide();
44520         }
44521         this.fireEvent("slidehide", this);
44522     },
44523
44524     slideIn : function(cb){
44525         if(!this.isSlid || this.el.hasActiveFx()){
44526             Roo.callback(cb);
44527             return;
44528         }
44529         this.isSlid = false;
44530         this.beforeSlide();
44531         this.el.slideOut(this.getSlideAnchor(), {
44532             callback: function(){
44533                 this.el.setLeftTop(-10000, -10000);
44534                 this.afterSlide();
44535                 this.afterSlideIn();
44536                 Roo.callback(cb);
44537             },
44538             scope: this,
44539             block: true
44540         });
44541     },
44542     
44543     slideInIf : function(e){
44544         if(!e.within(this.el)){
44545             this.slideIn();
44546         }
44547     },
44548
44549     animateCollapse : function(){
44550         this.beforeSlide();
44551         this.el.setStyle("z-index", 20000);
44552         var anchor = this.getSlideAnchor();
44553         this.el.slideOut(anchor, {
44554             callback : function(){
44555                 this.el.setStyle("z-index", "");
44556                 this.collapsedEl.slideIn(anchor, {duration:.3});
44557                 this.afterSlide();
44558                 this.el.setLocation(-10000,-10000);
44559                 this.el.hide();
44560                 this.fireEvent("collapsed", this);
44561             },
44562             scope: this,
44563             block: true
44564         });
44565     },
44566
44567     animateExpand : function(){
44568         this.beforeSlide();
44569         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44570         this.el.setStyle("z-index", 20000);
44571         this.collapsedEl.hide({
44572             duration:.1
44573         });
44574         this.el.slideIn(this.getSlideAnchor(), {
44575             callback : function(){
44576                 this.el.setStyle("z-index", "");
44577                 this.afterSlide();
44578                 if(this.split){
44579                     this.split.el.show();
44580                 }
44581                 this.fireEvent("invalidated", this);
44582                 this.fireEvent("expanded", this);
44583             },
44584             scope: this,
44585             block: true
44586         });
44587     },
44588
44589     anchors : {
44590         "west" : "left",
44591         "east" : "right",
44592         "north" : "top",
44593         "south" : "bottom"
44594     },
44595
44596     sanchors : {
44597         "west" : "l",
44598         "east" : "r",
44599         "north" : "t",
44600         "south" : "b"
44601     },
44602
44603     canchors : {
44604         "west" : "tl-tr",
44605         "east" : "tr-tl",
44606         "north" : "tl-bl",
44607         "south" : "bl-tl"
44608     },
44609
44610     getAnchor : function(){
44611         return this.anchors[this.position];
44612     },
44613
44614     getCollapseAnchor : function(){
44615         return this.canchors[this.position];
44616     },
44617
44618     getSlideAnchor : function(){
44619         return this.sanchors[this.position];
44620     },
44621
44622     getAlignAdj : function(){
44623         var cm = this.cmargins;
44624         switch(this.position){
44625             case "west":
44626                 return [0, 0];
44627             break;
44628             case "east":
44629                 return [0, 0];
44630             break;
44631             case "north":
44632                 return [0, 0];
44633             break;
44634             case "south":
44635                 return [0, 0];
44636             break;
44637         }
44638     },
44639
44640     getExpandAdj : function(){
44641         var c = this.collapsedEl, cm = this.cmargins;
44642         switch(this.position){
44643             case "west":
44644                 return [-(cm.right+c.getWidth()+cm.left), 0];
44645             break;
44646             case "east":
44647                 return [cm.right+c.getWidth()+cm.left, 0];
44648             break;
44649             case "north":
44650                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44651             break;
44652             case "south":
44653                 return [0, cm.top+cm.bottom+c.getHeight()];
44654             break;
44655         }
44656     }
44657 });/*
44658  * Based on:
44659  * Ext JS Library 1.1.1
44660  * Copyright(c) 2006-2007, Ext JS, LLC.
44661  *
44662  * Originally Released Under LGPL - original licence link has changed is not relivant.
44663  *
44664  * Fork - LGPL
44665  * <script type="text/javascript">
44666  */
44667 /*
44668  * These classes are private internal classes
44669  */
44670 Roo.bootstrap.layout.Center = function(config){
44671     config.region = "center";
44672     Roo.bootstrap.layout.Region.call(this, config);
44673     this.visible = true;
44674     this.minWidth = config.minWidth || 20;
44675     this.minHeight = config.minHeight || 20;
44676 };
44677
44678 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44679     hide : function(){
44680         // center panel can't be hidden
44681     },
44682     
44683     show : function(){
44684         // center panel can't be hidden
44685     },
44686     
44687     getMinWidth: function(){
44688         return this.minWidth;
44689     },
44690     
44691     getMinHeight: function(){
44692         return this.minHeight;
44693     }
44694 });
44695
44696
44697
44698
44699  
44700
44701
44702
44703
44704
44705
44706 Roo.bootstrap.layout.North = function(config)
44707 {
44708     config.region = 'north';
44709     config.cursor = 'n-resize';
44710     
44711     Roo.bootstrap.layout.Split.call(this, config);
44712     
44713     
44714     if(this.split){
44715         this.split.placement = Roo.bootstrap.SplitBar.TOP;
44716         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44717         this.split.el.addClass("roo-layout-split-v");
44718     }
44719     //var size = config.initialSize || config.height;
44720     //if(this.el && typeof size != "undefined"){
44721     //    this.el.setHeight(size);
44722     //}
44723 };
44724 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44725 {
44726     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44727      
44728      
44729     onRender : function(ctr, pos)
44730     {
44731         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44732         var size = this.config.initialSize || this.config.height;
44733         if(this.el && typeof size != "undefined"){
44734             this.el.setHeight(size);
44735         }
44736     
44737     },
44738     
44739     getBox : function(){
44740         if(this.collapsed){
44741             return this.collapsedEl.getBox();
44742         }
44743         var box = this.el.getBox();
44744         if(this.split){
44745             box.height += this.split.el.getHeight();
44746         }
44747         return box;
44748     },
44749     
44750     updateBox : function(box){
44751         if(this.split && !this.collapsed){
44752             box.height -= this.split.el.getHeight();
44753             this.split.el.setLeft(box.x);
44754             this.split.el.setTop(box.y+box.height);
44755             this.split.el.setWidth(box.width);
44756         }
44757         if(this.collapsed){
44758             this.updateBody(box.width, null);
44759         }
44760         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44761     }
44762 });
44763
44764
44765
44766
44767
44768 Roo.bootstrap.layout.South = function(config){
44769     config.region = 'south';
44770     config.cursor = 's-resize';
44771     Roo.bootstrap.layout.Split.call(this, config);
44772     if(this.split){
44773         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44774         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44775         this.split.el.addClass("roo-layout-split-v");
44776     }
44777     
44778 };
44779
44780 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44781     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44782     
44783     onRender : function(ctr, pos)
44784     {
44785         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44786         var size = this.config.initialSize || this.config.height;
44787         if(this.el && typeof size != "undefined"){
44788             this.el.setHeight(size);
44789         }
44790     
44791     },
44792     
44793     getBox : function(){
44794         if(this.collapsed){
44795             return this.collapsedEl.getBox();
44796         }
44797         var box = this.el.getBox();
44798         if(this.split){
44799             var sh = this.split.el.getHeight();
44800             box.height += sh;
44801             box.y -= sh;
44802         }
44803         return box;
44804     },
44805     
44806     updateBox : function(box){
44807         if(this.split && !this.collapsed){
44808             var sh = this.split.el.getHeight();
44809             box.height -= sh;
44810             box.y += sh;
44811             this.split.el.setLeft(box.x);
44812             this.split.el.setTop(box.y-sh);
44813             this.split.el.setWidth(box.width);
44814         }
44815         if(this.collapsed){
44816             this.updateBody(box.width, null);
44817         }
44818         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44819     }
44820 });
44821
44822 Roo.bootstrap.layout.East = function(config){
44823     config.region = "east";
44824     config.cursor = "e-resize";
44825     Roo.bootstrap.layout.Split.call(this, config);
44826     if(this.split){
44827         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44828         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44829         this.split.el.addClass("roo-layout-split-h");
44830     }
44831     
44832 };
44833 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44834     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44835     
44836     onRender : function(ctr, pos)
44837     {
44838         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44839         var size = this.config.initialSize || this.config.width;
44840         if(this.el && typeof size != "undefined"){
44841             this.el.setWidth(size);
44842         }
44843     
44844     },
44845     
44846     getBox : function(){
44847         if(this.collapsed){
44848             return this.collapsedEl.getBox();
44849         }
44850         var box = this.el.getBox();
44851         if(this.split){
44852             var sw = this.split.el.getWidth();
44853             box.width += sw;
44854             box.x -= sw;
44855         }
44856         return box;
44857     },
44858
44859     updateBox : function(box){
44860         if(this.split && !this.collapsed){
44861             var sw = this.split.el.getWidth();
44862             box.width -= sw;
44863             this.split.el.setLeft(box.x);
44864             this.split.el.setTop(box.y);
44865             this.split.el.setHeight(box.height);
44866             box.x += sw;
44867         }
44868         if(this.collapsed){
44869             this.updateBody(null, box.height);
44870         }
44871         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44872     }
44873 });
44874
44875 Roo.bootstrap.layout.West = function(config){
44876     config.region = "west";
44877     config.cursor = "w-resize";
44878     
44879     Roo.bootstrap.layout.Split.call(this, config);
44880     if(this.split){
44881         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44882         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44883         this.split.el.addClass("roo-layout-split-h");
44884     }
44885     
44886 };
44887 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44888     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44889     
44890     onRender: function(ctr, pos)
44891     {
44892         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44893         var size = this.config.initialSize || this.config.width;
44894         if(typeof size != "undefined"){
44895             this.el.setWidth(size);
44896         }
44897     },
44898     
44899     getBox : function(){
44900         if(this.collapsed){
44901             return this.collapsedEl.getBox();
44902         }
44903         var box = this.el.getBox();
44904         if (box.width == 0) {
44905             box.width = this.config.width; // kludge?
44906         }
44907         if(this.split){
44908             box.width += this.split.el.getWidth();
44909         }
44910         return box;
44911     },
44912     
44913     updateBox : function(box){
44914         if(this.split && !this.collapsed){
44915             var sw = this.split.el.getWidth();
44916             box.width -= sw;
44917             this.split.el.setLeft(box.x+box.width);
44918             this.split.el.setTop(box.y);
44919             this.split.el.setHeight(box.height);
44920         }
44921         if(this.collapsed){
44922             this.updateBody(null, box.height);
44923         }
44924         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44925     }
44926 });/*
44927  * Based on:
44928  * Ext JS Library 1.1.1
44929  * Copyright(c) 2006-2007, Ext JS, LLC.
44930  *
44931  * Originally Released Under LGPL - original licence link has changed is not relivant.
44932  *
44933  * Fork - LGPL
44934  * <script type="text/javascript">
44935  */
44936 /**
44937  * @class Roo.bootstrap.paenl.Content
44938  * @extends Roo.util.Observable
44939  * @children Roo.bootstrap.Component
44940  * @parent builder Roo.bootstrap.layout.Border
44941  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44942  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44943  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44944  * @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
44945  * @cfg {Boolean}   closable      True if the panel can be closed/removed
44946  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
44947  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44948  * @cfg {Toolbar}   toolbar       A toolbar for this panel
44949  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
44950  * @cfg {String} title          The title for this panel
44951  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44952  * @cfg {String} url            Calls {@link #setUrl} with this value
44953  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44954  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
44955  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
44956  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
44957  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
44958  * @cfg {Boolean} badges render the badges
44959  * @cfg {String} cls  extra classes to use  
44960  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44961  
44962  * @constructor
44963  * Create a new ContentPanel.
44964  * @param {String/Object} config A string to set only the title or a config object
44965  
44966  */
44967 Roo.bootstrap.panel.Content = function( config){
44968     
44969     this.tpl = config.tpl || false;
44970     
44971     var el = config.el;
44972     var content = config.content;
44973
44974     if(config.autoCreate){ // xtype is available if this is called from factory
44975         el = Roo.id();
44976     }
44977     this.el = Roo.get(el);
44978     if(!this.el && config && config.autoCreate){
44979         if(typeof config.autoCreate == "object"){
44980             if(!config.autoCreate.id){
44981                 config.autoCreate.id = config.id||el;
44982             }
44983             this.el = Roo.DomHelper.append(document.body,
44984                         config.autoCreate, true);
44985         }else{
44986             var elcfg =  {
44987                 tag: "div",
44988                 cls: (config.cls || '') +
44989                     (config.background ? ' bg-' + config.background : '') +
44990                     " roo-layout-inactive-content",
44991                 id: config.id||el
44992             };
44993             if (config.iframe) {
44994                 elcfg.cn = [
44995                     {
44996                         tag : 'iframe',
44997                         style : 'border: 0px',
44998                         src : 'about:blank'
44999                     }
45000                 ];
45001             }
45002               
45003             if (config.html) {
45004                 elcfg.html = config.html;
45005                 
45006             }
45007                         
45008             this.el = Roo.DomHelper.append(document.body, elcfg , true);
45009             if (config.iframe) {
45010                 this.iframeEl = this.el.select('iframe',true).first();
45011             }
45012             
45013         }
45014     } 
45015     this.closable = false;
45016     this.loaded = false;
45017     this.active = false;
45018    
45019       
45020     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
45021         
45022         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45023         
45024         this.wrapEl = this.el; //this.el.wrap();
45025         var ti = [];
45026         if (config.toolbar.items) {
45027             ti = config.toolbar.items ;
45028             delete config.toolbar.items ;
45029         }
45030         
45031         var nitems = [];
45032         this.toolbar.render(this.wrapEl, 'before');
45033         for(var i =0;i < ti.length;i++) {
45034           //  Roo.log(['add child', items[i]]);
45035             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45036         }
45037         this.toolbar.items = nitems;
45038         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45039         delete config.toolbar;
45040         
45041     }
45042     /*
45043     // xtype created footer. - not sure if will work as we normally have to render first..
45044     if (this.footer && !this.footer.el && this.footer.xtype) {
45045         if (!this.wrapEl) {
45046             this.wrapEl = this.el.wrap();
45047         }
45048     
45049         this.footer.container = this.wrapEl.createChild();
45050          
45051         this.footer = Roo.factory(this.footer, Roo);
45052         
45053     }
45054     */
45055     
45056      if(typeof config == "string"){
45057         this.title = config;
45058     }else{
45059         Roo.apply(this, config);
45060     }
45061     
45062     if(this.resizeEl){
45063         this.resizeEl = Roo.get(this.resizeEl, true);
45064     }else{
45065         this.resizeEl = this.el;
45066     }
45067     // handle view.xtype
45068     
45069  
45070     
45071     
45072     this.addEvents({
45073         /**
45074          * @event activate
45075          * Fires when this panel is activated. 
45076          * @param {Roo.ContentPanel} this
45077          */
45078         "activate" : true,
45079         /**
45080          * @event deactivate
45081          * Fires when this panel is activated. 
45082          * @param {Roo.ContentPanel} this
45083          */
45084         "deactivate" : true,
45085
45086         /**
45087          * @event resize
45088          * Fires when this panel is resized if fitToFrame is true.
45089          * @param {Roo.ContentPanel} this
45090          * @param {Number} width The width after any component adjustments
45091          * @param {Number} height The height after any component adjustments
45092          */
45093         "resize" : true,
45094         
45095          /**
45096          * @event render
45097          * Fires when this tab is created
45098          * @param {Roo.ContentPanel} this
45099          */
45100         "render" : true,
45101         
45102           /**
45103          * @event scroll
45104          * Fires when this content is scrolled
45105          * @param {Roo.ContentPanel} this
45106          * @param {Event} scrollEvent
45107          */
45108         "scroll" : true
45109         
45110         
45111         
45112     });
45113     
45114
45115     
45116     
45117     if(this.autoScroll && !this.iframe){
45118         this.resizeEl.setStyle("overflow", "auto");
45119         this.resizeEl.on('scroll', this.onScroll, this);
45120     } else {
45121         // fix randome scrolling
45122         //this.el.on('scroll', function() {
45123         //    Roo.log('fix random scolling');
45124         //    this.scrollTo('top',0); 
45125         //});
45126     }
45127     content = content || this.content;
45128     if(content){
45129         this.setContent(content);
45130     }
45131     if(config && config.url){
45132         this.setUrl(this.url, this.params, this.loadOnce);
45133     }
45134     
45135     
45136     
45137     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45138     
45139     if (this.view && typeof(this.view.xtype) != 'undefined') {
45140         this.view.el = this.el.appendChild(document.createElement("div"));
45141         this.view = Roo.factory(this.view); 
45142         this.view.render  &&  this.view.render(false, '');  
45143     }
45144     
45145     
45146     this.fireEvent('render', this);
45147 };
45148
45149 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45150     
45151     cls : '',
45152     background : '',
45153     
45154     tabTip : '',
45155     
45156     iframe : false,
45157     iframeEl : false,
45158     
45159     /* Resize Element - use this to work out scroll etc. */
45160     resizeEl : false,
45161     
45162     setRegion : function(region){
45163         this.region = region;
45164         this.setActiveClass(region && !this.background);
45165     },
45166     
45167     
45168     setActiveClass: function(state)
45169     {
45170         if(state){
45171            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45172            this.el.setStyle('position','relative');
45173         }else{
45174            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45175            this.el.setStyle('position', 'absolute');
45176         } 
45177     },
45178     
45179     /**
45180      * Returns the toolbar for this Panel if one was configured. 
45181      * @return {Roo.Toolbar} 
45182      */
45183     getToolbar : function(){
45184         return this.toolbar;
45185     },
45186     
45187     setActiveState : function(active)
45188     {
45189         this.active = active;
45190         this.setActiveClass(active);
45191         if(!active){
45192             if(this.fireEvent("deactivate", this) === false){
45193                 return false;
45194             }
45195             return true;
45196         }
45197         this.fireEvent("activate", this);
45198         return true;
45199     },
45200     /**
45201      * Updates this panel's element (not for iframe)
45202      * @param {String} content The new content
45203      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45204     */
45205     setContent : function(content, loadScripts){
45206         if (this.iframe) {
45207             return;
45208         }
45209         
45210         this.el.update(content, loadScripts);
45211     },
45212
45213     ignoreResize : function(w, h)
45214     {
45215         //return false; // always resize?
45216         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45217             return true;
45218         }else{
45219             this.lastSize = {width: w, height: h};
45220             return false;
45221         }
45222     },
45223     /**
45224      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45225      * @return {Roo.UpdateManager} The UpdateManager
45226      */
45227     getUpdateManager : function(){
45228         if (this.iframe) {
45229             return false;
45230         }
45231         return this.el.getUpdateManager();
45232     },
45233      /**
45234      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45235      * Does not work with IFRAME contents
45236      * @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:
45237 <pre><code>
45238 panel.load({
45239     url: "your-url.php",
45240     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45241     callback: yourFunction,
45242     scope: yourObject, //(optional scope)
45243     discardUrl: false,
45244     nocache: false,
45245     text: "Loading...",
45246     timeout: 30,
45247     scripts: false
45248 });
45249 </code></pre>
45250      
45251      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45252      * 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.
45253      * @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}
45254      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45255      * @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.
45256      * @return {Roo.ContentPanel} this
45257      */
45258     load : function(){
45259         
45260         if (this.iframe) {
45261             return this;
45262         }
45263         
45264         var um = this.el.getUpdateManager();
45265         um.update.apply(um, arguments);
45266         return this;
45267     },
45268
45269
45270     /**
45271      * 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.
45272      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45273      * @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)
45274      * @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)
45275      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45276      */
45277     setUrl : function(url, params, loadOnce){
45278         if (this.iframe) {
45279             this.iframeEl.dom.src = url;
45280             return false;
45281         }
45282         
45283         if(this.refreshDelegate){
45284             this.removeListener("activate", this.refreshDelegate);
45285         }
45286         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45287         this.on("activate", this.refreshDelegate);
45288         return this.el.getUpdateManager();
45289     },
45290     
45291     _handleRefresh : function(url, params, loadOnce){
45292         if(!loadOnce || !this.loaded){
45293             var updater = this.el.getUpdateManager();
45294             updater.update(url, params, this._setLoaded.createDelegate(this));
45295         }
45296     },
45297     
45298     _setLoaded : function(){
45299         this.loaded = true;
45300     }, 
45301     
45302     /**
45303      * Returns this panel's id
45304      * @return {String} 
45305      */
45306     getId : function(){
45307         return this.el.id;
45308     },
45309     
45310     /** 
45311      * Returns this panel's element - used by regiosn to add.
45312      * @return {Roo.Element} 
45313      */
45314     getEl : function(){
45315         return this.wrapEl || this.el;
45316     },
45317     
45318    
45319     
45320     adjustForComponents : function(width, height)
45321     {
45322         //Roo.log('adjustForComponents ');
45323         if(this.resizeEl != this.el){
45324             width -= this.el.getFrameWidth('lr');
45325             height -= this.el.getFrameWidth('tb');
45326         }
45327         if(this.toolbar){
45328             var te = this.toolbar.getEl();
45329             te.setWidth(width);
45330             height -= te.getHeight();
45331         }
45332         if(this.footer){
45333             var te = this.footer.getEl();
45334             te.setWidth(width);
45335             height -= te.getHeight();
45336         }
45337         
45338         
45339         if(this.adjustments){
45340             width += this.adjustments[0];
45341             height += this.adjustments[1];
45342         }
45343         return {"width": width, "height": height};
45344     },
45345     
45346     setSize : function(width, height){
45347         if(this.fitToFrame && !this.ignoreResize(width, height)){
45348             if(this.fitContainer && this.resizeEl != this.el){
45349                 this.el.setSize(width, height);
45350             }
45351             var size = this.adjustForComponents(width, height);
45352             if (this.iframe) {
45353                 this.iframeEl.setSize(width,height);
45354             }
45355             
45356             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45357             this.fireEvent('resize', this, size.width, size.height);
45358             
45359             
45360         }
45361     },
45362     
45363     /**
45364      * Returns this panel's title
45365      * @return {String} 
45366      */
45367     getTitle : function(){
45368         
45369         if (typeof(this.title) != 'object') {
45370             return this.title;
45371         }
45372         
45373         var t = '';
45374         for (var k in this.title) {
45375             if (!this.title.hasOwnProperty(k)) {
45376                 continue;
45377             }
45378             
45379             if (k.indexOf('-') >= 0) {
45380                 var s = k.split('-');
45381                 for (var i = 0; i<s.length; i++) {
45382                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45383                 }
45384             } else {
45385                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45386             }
45387         }
45388         return t;
45389     },
45390     
45391     /**
45392      * Set this panel's title
45393      * @param {String} title
45394      */
45395     setTitle : function(title){
45396         this.title = title;
45397         if(this.region){
45398             this.region.updatePanelTitle(this, title);
45399         }
45400     },
45401     
45402     /**
45403      * Returns true is this panel was configured to be closable
45404      * @return {Boolean} 
45405      */
45406     isClosable : function(){
45407         return this.closable;
45408     },
45409     
45410     beforeSlide : function(){
45411         this.el.clip();
45412         this.resizeEl.clip();
45413     },
45414     
45415     afterSlide : function(){
45416         this.el.unclip();
45417         this.resizeEl.unclip();
45418     },
45419     
45420     /**
45421      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45422      *   Will fail silently if the {@link #setUrl} method has not been called.
45423      *   This does not activate the panel, just updates its content.
45424      */
45425     refresh : function(){
45426         if(this.refreshDelegate){
45427            this.loaded = false;
45428            this.refreshDelegate();
45429         }
45430     },
45431     
45432     /**
45433      * Destroys this panel
45434      */
45435     destroy : function(){
45436         this.el.removeAllListeners();
45437         var tempEl = document.createElement("span");
45438         tempEl.appendChild(this.el.dom);
45439         tempEl.innerHTML = "";
45440         this.el.remove();
45441         this.el = null;
45442     },
45443     
45444     /**
45445      * form - if the content panel contains a form - this is a reference to it.
45446      * @type {Roo.form.Form}
45447      */
45448     form : false,
45449     /**
45450      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45451      *    This contains a reference to it.
45452      * @type {Roo.View}
45453      */
45454     view : false,
45455     
45456       /**
45457      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45458      * <pre><code>
45459
45460 layout.addxtype({
45461        xtype : 'Form',
45462        items: [ .... ]
45463    }
45464 );
45465
45466 </code></pre>
45467      * @param {Object} cfg Xtype definition of item to add.
45468      */
45469     
45470     
45471     getChildContainer: function () {
45472         return this.getEl();
45473     },
45474     
45475     
45476     onScroll : function(e)
45477     {
45478         this.fireEvent('scroll', this, e);
45479     }
45480     
45481     
45482     /*
45483         var  ret = new Roo.factory(cfg);
45484         return ret;
45485         
45486         
45487         // add form..
45488         if (cfg.xtype.match(/^Form$/)) {
45489             
45490             var el;
45491             //if (this.footer) {
45492             //    el = this.footer.container.insertSibling(false, 'before');
45493             //} else {
45494                 el = this.el.createChild();
45495             //}
45496
45497             this.form = new  Roo.form.Form(cfg);
45498             
45499             
45500             if ( this.form.allItems.length) {
45501                 this.form.render(el.dom);
45502             }
45503             return this.form;
45504         }
45505         // should only have one of theses..
45506         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45507             // views.. should not be just added - used named prop 'view''
45508             
45509             cfg.el = this.el.appendChild(document.createElement("div"));
45510             // factory?
45511             
45512             var ret = new Roo.factory(cfg);
45513              
45514              ret.render && ret.render(false, ''); // render blank..
45515             this.view = ret;
45516             return ret;
45517         }
45518         return false;
45519     }
45520     \*/
45521 });
45522  
45523 /**
45524  * @class Roo.bootstrap.panel.Grid
45525  * @extends Roo.bootstrap.panel.Content
45526  * @constructor
45527  * Create a new GridPanel.
45528  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45529  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45530  * @param {Object} config A the config object
45531   
45532  */
45533
45534
45535
45536 Roo.bootstrap.panel.Grid = function(config)
45537 {
45538     
45539       
45540     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45541         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45542
45543     config.el = this.wrapper;
45544     //this.el = this.wrapper;
45545     
45546       if (config.container) {
45547         // ctor'ed from a Border/panel.grid
45548         
45549         
45550         this.wrapper.setStyle("overflow", "hidden");
45551         this.wrapper.addClass('roo-grid-container');
45552
45553     }
45554     
45555     
45556     if(config.toolbar){
45557         var tool_el = this.wrapper.createChild();    
45558         this.toolbar = Roo.factory(config.toolbar);
45559         var ti = [];
45560         if (config.toolbar.items) {
45561             ti = config.toolbar.items ;
45562             delete config.toolbar.items ;
45563         }
45564         
45565         var nitems = [];
45566         this.toolbar.render(tool_el);
45567         for(var i =0;i < ti.length;i++) {
45568           //  Roo.log(['add child', items[i]]);
45569             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45570         }
45571         this.toolbar.items = nitems;
45572         
45573         delete config.toolbar;
45574     }
45575     
45576     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45577     config.grid.scrollBody = true;;
45578     config.grid.monitorWindowResize = false; // turn off autosizing
45579     config.grid.autoHeight = false;
45580     config.grid.autoWidth = false;
45581     
45582     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45583     
45584     if (config.background) {
45585         // render grid on panel activation (if panel background)
45586         this.on('activate', function(gp) {
45587             if (!gp.grid.rendered) {
45588                 gp.grid.render(this.wrapper);
45589                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45590             }
45591         });
45592             
45593     } else {
45594         this.grid.render(this.wrapper);
45595         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45596
45597     }
45598     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45599     // ??? needed ??? config.el = this.wrapper;
45600     
45601     
45602     
45603   
45604     // xtype created footer. - not sure if will work as we normally have to render first..
45605     if (this.footer && !this.footer.el && this.footer.xtype) {
45606         
45607         var ctr = this.grid.getView().getFooterPanel(true);
45608         this.footer.dataSource = this.grid.dataSource;
45609         this.footer = Roo.factory(this.footer, Roo);
45610         this.footer.render(ctr);
45611         
45612     }
45613     
45614     
45615     
45616     
45617      
45618 };
45619
45620 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45621 {
45622   
45623     getId : function(){
45624         return this.grid.id;
45625     },
45626     
45627     /**
45628      * Returns the grid for this panel
45629      * @return {Roo.bootstrap.Table} 
45630      */
45631     getGrid : function(){
45632         return this.grid;    
45633     },
45634     
45635     setSize : function(width, height)
45636     {
45637      
45638         //if(!this.ignoreResize(width, height)){
45639             var grid = this.grid;
45640             var size = this.adjustForComponents(width, height);
45641             // tfoot is not a footer?
45642           
45643             
45644             var gridel = grid.getGridEl();
45645             gridel.setSize(size.width, size.height);
45646             
45647             var tbd = grid.getGridEl().select('tbody', true).first();
45648             var thd = grid.getGridEl().select('thead',true).first();
45649             var tbf= grid.getGridEl().select('tfoot', true).first();
45650
45651             if (tbf) {
45652                 size.height -= tbf.getHeight();
45653             }
45654             if (thd) {
45655                 size.height -= thd.getHeight();
45656             }
45657             
45658             tbd.setSize(size.width, size.height );
45659             // this is for the account management tab -seems to work there.
45660             var thd = grid.getGridEl().select('thead',true).first();
45661             //if (tbd) {
45662             //    tbd.setSize(size.width, size.height - thd.getHeight());
45663             //}
45664              
45665             grid.autoSize();
45666         //}
45667    
45668     },
45669      
45670     
45671     
45672     beforeSlide : function(){
45673         this.grid.getView().scroller.clip();
45674     },
45675     
45676     afterSlide : function(){
45677         this.grid.getView().scroller.unclip();
45678     },
45679     
45680     destroy : function(){
45681         this.grid.destroy();
45682         delete this.grid;
45683         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
45684     }
45685 });
45686
45687 /**
45688  * @class Roo.bootstrap.panel.Nest
45689  * @extends Roo.bootstrap.panel.Content
45690  * @constructor
45691  * Create a new Panel, that can contain a layout.Border.
45692  * 
45693  * 
45694  * @param {String/Object} config A string to set only the title or a config object
45695  */
45696 Roo.bootstrap.panel.Nest = function(config)
45697 {
45698     // construct with only one argument..
45699     /* FIXME - implement nicer consturctors
45700     if (layout.layout) {
45701         config = layout;
45702         layout = config.layout;
45703         delete config.layout;
45704     }
45705     if (layout.xtype && !layout.getEl) {
45706         // then layout needs constructing..
45707         layout = Roo.factory(layout, Roo);
45708     }
45709     */
45710     
45711     config.el =  config.layout.getEl();
45712     
45713     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45714     
45715     config.layout.monitorWindowResize = false; // turn off autosizing
45716     this.layout = config.layout;
45717     this.layout.getEl().addClass("roo-layout-nested-layout");
45718     this.layout.parent = this;
45719     
45720     
45721     
45722     
45723 };
45724
45725 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45726     /**
45727     * @cfg {Roo.BorderLayout} layout The layout for this panel
45728     */
45729     layout : false,
45730
45731     setSize : function(width, height){
45732         if(!this.ignoreResize(width, height)){
45733             var size = this.adjustForComponents(width, height);
45734             var el = this.layout.getEl();
45735             if (size.height < 1) {
45736                 el.setWidth(size.width);   
45737             } else {
45738                 el.setSize(size.width, size.height);
45739             }
45740             var touch = el.dom.offsetWidth;
45741             this.layout.layout();
45742             // ie requires a double layout on the first pass
45743             if(Roo.isIE && !this.initialized){
45744                 this.initialized = true;
45745                 this.layout.layout();
45746             }
45747         }
45748     },
45749     
45750     // activate all subpanels if not currently active..
45751     
45752     setActiveState : function(active){
45753         this.active = active;
45754         this.setActiveClass(active);
45755         
45756         if(!active){
45757             this.fireEvent("deactivate", this);
45758             return;
45759         }
45760         
45761         this.fireEvent("activate", this);
45762         // not sure if this should happen before or after..
45763         if (!this.layout) {
45764             return; // should not happen..
45765         }
45766         var reg = false;
45767         for (var r in this.layout.regions) {
45768             reg = this.layout.getRegion(r);
45769             if (reg.getActivePanel()) {
45770                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45771                 reg.setActivePanel(reg.getActivePanel());
45772                 continue;
45773             }
45774             if (!reg.panels.length) {
45775                 continue;
45776             }
45777             reg.showPanel(reg.getPanel(0));
45778         }
45779         
45780         
45781         
45782         
45783     },
45784     
45785     /**
45786      * Returns the nested BorderLayout for this panel
45787      * @return {Roo.BorderLayout} 
45788      */
45789     getLayout : function(){
45790         return this.layout;
45791     },
45792     
45793      /**
45794      * Adds a xtype elements to the layout of the nested panel
45795      * <pre><code>
45796
45797 panel.addxtype({
45798        xtype : 'ContentPanel',
45799        region: 'west',
45800        items: [ .... ]
45801    }
45802 );
45803
45804 panel.addxtype({
45805         xtype : 'NestedLayoutPanel',
45806         region: 'west',
45807         layout: {
45808            center: { },
45809            west: { }   
45810         },
45811         items : [ ... list of content panels or nested layout panels.. ]
45812    }
45813 );
45814 </code></pre>
45815      * @param {Object} cfg Xtype definition of item to add.
45816      */
45817     addxtype : function(cfg) {
45818         return this.layout.addxtype(cfg);
45819     
45820     }
45821 });/*
45822  * Based on:
45823  * Ext JS Library 1.1.1
45824  * Copyright(c) 2006-2007, Ext JS, LLC.
45825  *
45826  * Originally Released Under LGPL - original licence link has changed is not relivant.
45827  *
45828  * Fork - LGPL
45829  * <script type="text/javascript">
45830  */
45831 /**
45832  * @class Roo.TabPanel
45833  * @extends Roo.util.Observable
45834  * A lightweight tab container.
45835  * <br><br>
45836  * Usage:
45837  * <pre><code>
45838 // basic tabs 1, built from existing content
45839 var tabs = new Roo.TabPanel("tabs1");
45840 tabs.addTab("script", "View Script");
45841 tabs.addTab("markup", "View Markup");
45842 tabs.activate("script");
45843
45844 // more advanced tabs, built from javascript
45845 var jtabs = new Roo.TabPanel("jtabs");
45846 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45847
45848 // set up the UpdateManager
45849 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45850 var updater = tab2.getUpdateManager();
45851 updater.setDefaultUrl("ajax1.htm");
45852 tab2.on('activate', updater.refresh, updater, true);
45853
45854 // Use setUrl for Ajax loading
45855 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45856 tab3.setUrl("ajax2.htm", null, true);
45857
45858 // Disabled tab
45859 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45860 tab4.disable();
45861
45862 jtabs.activate("jtabs-1");
45863  * </code></pre>
45864  * @constructor
45865  * Create a new TabPanel.
45866  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45867  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45868  */
45869 Roo.bootstrap.panel.Tabs = function(config){
45870     /**
45871     * The container element for this TabPanel.
45872     * @type Roo.Element
45873     */
45874     this.el = Roo.get(config.el);
45875     delete config.el;
45876     if(config){
45877         if(typeof config == "boolean"){
45878             this.tabPosition = config ? "bottom" : "top";
45879         }else{
45880             Roo.apply(this, config);
45881         }
45882     }
45883     
45884     if(this.tabPosition == "bottom"){
45885         // if tabs are at the bottom = create the body first.
45886         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45887         this.el.addClass("roo-tabs-bottom");
45888     }
45889     // next create the tabs holders
45890     
45891     if (this.tabPosition == "west"){
45892         
45893         var reg = this.region; // fake it..
45894         while (reg) {
45895             if (!reg.mgr.parent) {
45896                 break;
45897             }
45898             reg = reg.mgr.parent.region;
45899         }
45900         Roo.log("got nest?");
45901         Roo.log(reg);
45902         if (reg.mgr.getRegion('west')) {
45903             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45904             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45905             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45906             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45907             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45908         
45909             
45910         }
45911         
45912         
45913     } else {
45914      
45915         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45916         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45917         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45918         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45919     }
45920     
45921     
45922     if(Roo.isIE){
45923         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45924     }
45925     
45926     // finally - if tabs are at the top, then create the body last..
45927     if(this.tabPosition != "bottom"){
45928         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45929          * @type Roo.Element
45930          */
45931         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45932         this.el.addClass("roo-tabs-top");
45933     }
45934     this.items = [];
45935
45936     this.bodyEl.setStyle("position", "relative");
45937
45938     this.active = null;
45939     this.activateDelegate = this.activate.createDelegate(this);
45940
45941     this.addEvents({
45942         /**
45943          * @event tabchange
45944          * Fires when the active tab changes
45945          * @param {Roo.TabPanel} this
45946          * @param {Roo.TabPanelItem} activePanel The new active tab
45947          */
45948         "tabchange": true,
45949         /**
45950          * @event beforetabchange
45951          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45952          * @param {Roo.TabPanel} this
45953          * @param {Object} e Set cancel to true on this object to cancel the tab change
45954          * @param {Roo.TabPanelItem} tab The tab being changed to
45955          */
45956         "beforetabchange" : true
45957     });
45958
45959     Roo.EventManager.onWindowResize(this.onResize, this);
45960     this.cpad = this.el.getPadding("lr");
45961     this.hiddenCount = 0;
45962
45963
45964     // toolbar on the tabbar support...
45965     if (this.toolbar) {
45966         alert("no toolbar support yet");
45967         this.toolbar  = false;
45968         /*
45969         var tcfg = this.toolbar;
45970         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
45971         this.toolbar = new Roo.Toolbar(tcfg);
45972         if (Roo.isSafari) {
45973             var tbl = tcfg.container.child('table', true);
45974             tbl.setAttribute('width', '100%');
45975         }
45976         */
45977         
45978     }
45979    
45980
45981
45982     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45983 };
45984
45985 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45986     /*
45987      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45988      */
45989     tabPosition : "top",
45990     /*
45991      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45992      */
45993     currentTabWidth : 0,
45994     /*
45995      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45996      */
45997     minTabWidth : 40,
45998     /*
45999      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
46000      */
46001     maxTabWidth : 250,
46002     /*
46003      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
46004      */
46005     preferredTabWidth : 175,
46006     /*
46007      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
46008      */
46009     resizeTabs : false,
46010     /*
46011      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
46012      */
46013     monitorResize : true,
46014     /*
46015      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
46016      */
46017     toolbar : false,  // set by caller..
46018     
46019     region : false, /// set by caller
46020     
46021     disableTooltips : true, // not used yet...
46022
46023     /**
46024      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46025      * @param {String} id The id of the div to use <b>or create</b>
46026      * @param {String} text The text for the tab
46027      * @param {String} content (optional) Content to put in the TabPanelItem body
46028      * @param {Boolean} closable (optional) True to create a close icon on the tab
46029      * @return {Roo.TabPanelItem} The created TabPanelItem
46030      */
46031     addTab : function(id, text, content, closable, tpl)
46032     {
46033         var item = new Roo.bootstrap.panel.TabItem({
46034             panel: this,
46035             id : id,
46036             text : text,
46037             closable : closable,
46038             tpl : tpl
46039         });
46040         this.addTabItem(item);
46041         if(content){
46042             item.setContent(content);
46043         }
46044         return item;
46045     },
46046
46047     /**
46048      * Returns the {@link Roo.TabPanelItem} with the specified id/index
46049      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46050      * @return {Roo.TabPanelItem}
46051      */
46052     getTab : function(id){
46053         return this.items[id];
46054     },
46055
46056     /**
46057      * Hides the {@link Roo.TabPanelItem} with the specified id/index
46058      * @param {String/Number} id The id or index of the TabPanelItem to hide.
46059      */
46060     hideTab : function(id){
46061         var t = this.items[id];
46062         if(!t.isHidden()){
46063            t.setHidden(true);
46064            this.hiddenCount++;
46065            this.autoSizeTabs();
46066         }
46067     },
46068
46069     /**
46070      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46071      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46072      */
46073     unhideTab : function(id){
46074         var t = this.items[id];
46075         if(t.isHidden()){
46076            t.setHidden(false);
46077            this.hiddenCount--;
46078            this.autoSizeTabs();
46079         }
46080     },
46081
46082     /**
46083      * Adds an existing {@link Roo.TabPanelItem}.
46084      * @param {Roo.TabPanelItem} item The TabPanelItem to add
46085      */
46086     addTabItem : function(item)
46087     {
46088         this.items[item.id] = item;
46089         this.items.push(item);
46090         this.autoSizeTabs();
46091       //  if(this.resizeTabs){
46092     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46093   //         this.autoSizeTabs();
46094 //        }else{
46095 //            item.autoSize();
46096        // }
46097     },
46098
46099     /**
46100      * Removes a {@link Roo.TabPanelItem}.
46101      * @param {String/Number} id The id or index of the TabPanelItem to remove.
46102      */
46103     removeTab : function(id){
46104         var items = this.items;
46105         var tab = items[id];
46106         if(!tab) { return; }
46107         var index = items.indexOf(tab);
46108         if(this.active == tab && items.length > 1){
46109             var newTab = this.getNextAvailable(index);
46110             if(newTab) {
46111                 newTab.activate();
46112             }
46113         }
46114         this.stripEl.dom.removeChild(tab.pnode.dom);
46115         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46116             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46117         }
46118         items.splice(index, 1);
46119         delete this.items[tab.id];
46120         tab.fireEvent("close", tab);
46121         tab.purgeListeners();
46122         this.autoSizeTabs();
46123     },
46124
46125     getNextAvailable : function(start){
46126         var items = this.items;
46127         var index = start;
46128         // look for a next tab that will slide over to
46129         // replace the one being removed
46130         while(index < items.length){
46131             var item = items[++index];
46132             if(item && !item.isHidden()){
46133                 return item;
46134             }
46135         }
46136         // if one isn't found select the previous tab (on the left)
46137         index = start;
46138         while(index >= 0){
46139             var item = items[--index];
46140             if(item && !item.isHidden()){
46141                 return item;
46142             }
46143         }
46144         return null;
46145     },
46146
46147     /**
46148      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46149      * @param {String/Number} id The id or index of the TabPanelItem to disable.
46150      */
46151     disableTab : function(id){
46152         var tab = this.items[id];
46153         if(tab && this.active != tab){
46154             tab.disable();
46155         }
46156     },
46157
46158     /**
46159      * Enables a {@link Roo.TabPanelItem} that is disabled.
46160      * @param {String/Number} id The id or index of the TabPanelItem to enable.
46161      */
46162     enableTab : function(id){
46163         var tab = this.items[id];
46164         tab.enable();
46165     },
46166
46167     /**
46168      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46169      * @param {String/Number} id The id or index of the TabPanelItem to activate.
46170      * @return {Roo.TabPanelItem} The TabPanelItem.
46171      */
46172     activate : function(id)
46173     {
46174         //Roo.log('activite:'  + id);
46175         
46176         var tab = this.items[id];
46177         if(!tab){
46178             return null;
46179         }
46180         if(tab == this.active || tab.disabled){
46181             return tab;
46182         }
46183         var e = {};
46184         this.fireEvent("beforetabchange", this, e, tab);
46185         if(e.cancel !== true && !tab.disabled){
46186             if(this.active){
46187                 this.active.hide();
46188             }
46189             this.active = this.items[id];
46190             this.active.show();
46191             this.fireEvent("tabchange", this, this.active);
46192         }
46193         return tab;
46194     },
46195
46196     /**
46197      * Gets the active {@link Roo.TabPanelItem}.
46198      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46199      */
46200     getActiveTab : function(){
46201         return this.active;
46202     },
46203
46204     /**
46205      * Updates the tab body element to fit the height of the container element
46206      * for overflow scrolling
46207      * @param {Number} targetHeight (optional) Override the starting height from the elements height
46208      */
46209     syncHeight : function(targetHeight){
46210         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46211         var bm = this.bodyEl.getMargins();
46212         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46213         this.bodyEl.setHeight(newHeight);
46214         return newHeight;
46215     },
46216
46217     onResize : function(){
46218         if(this.monitorResize){
46219             this.autoSizeTabs();
46220         }
46221     },
46222
46223     /**
46224      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46225      */
46226     beginUpdate : function(){
46227         this.updating = true;
46228     },
46229
46230     /**
46231      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46232      */
46233     endUpdate : function(){
46234         this.updating = false;
46235         this.autoSizeTabs();
46236     },
46237
46238     /**
46239      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46240      */
46241     autoSizeTabs : function()
46242     {
46243         var count = this.items.length;
46244         var vcount = count - this.hiddenCount;
46245         
46246         if (vcount < 2) {
46247             this.stripEl.hide();
46248         } else {
46249             this.stripEl.show();
46250         }
46251         
46252         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46253             return;
46254         }
46255         
46256         
46257         var w = Math.max(this.el.getWidth() - this.cpad, 10);
46258         var availWidth = Math.floor(w / vcount);
46259         var b = this.stripBody;
46260         if(b.getWidth() > w){
46261             var tabs = this.items;
46262             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46263             if(availWidth < this.minTabWidth){
46264                 /*if(!this.sleft){    // incomplete scrolling code
46265                     this.createScrollButtons();
46266                 }
46267                 this.showScroll();
46268                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46269             }
46270         }else{
46271             if(this.currentTabWidth < this.preferredTabWidth){
46272                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46273             }
46274         }
46275     },
46276
46277     /**
46278      * Returns the number of tabs in this TabPanel.
46279      * @return {Number}
46280      */
46281      getCount : function(){
46282          return this.items.length;
46283      },
46284
46285     /**
46286      * Resizes all the tabs to the passed width
46287      * @param {Number} The new width
46288      */
46289     setTabWidth : function(width){
46290         this.currentTabWidth = width;
46291         for(var i = 0, len = this.items.length; i < len; i++) {
46292                 if(!this.items[i].isHidden()) {
46293                 this.items[i].setWidth(width);
46294             }
46295         }
46296     },
46297
46298     /**
46299      * Destroys this TabPanel
46300      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46301      */
46302     destroy : function(removeEl){
46303         Roo.EventManager.removeResizeListener(this.onResize, this);
46304         for(var i = 0, len = this.items.length; i < len; i++){
46305             this.items[i].purgeListeners();
46306         }
46307         if(removeEl === true){
46308             this.el.update("");
46309             this.el.remove();
46310         }
46311     },
46312     
46313     createStrip : function(container)
46314     {
46315         var strip = document.createElement("nav");
46316         strip.className = Roo.bootstrap.version == 4 ?
46317             "navbar-light bg-light" : 
46318             "navbar navbar-default"; //"x-tabs-wrap";
46319         container.appendChild(strip);
46320         return strip;
46321     },
46322     
46323     createStripList : function(strip)
46324     {
46325         // div wrapper for retard IE
46326         // returns the "tr" element.
46327         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46328         //'<div class="x-tabs-strip-wrap">'+
46329           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46330           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46331         return strip.firstChild; //.firstChild.firstChild.firstChild;
46332     },
46333     createBody : function(container)
46334     {
46335         var body = document.createElement("div");
46336         Roo.id(body, "tab-body");
46337         //Roo.fly(body).addClass("x-tabs-body");
46338         Roo.fly(body).addClass("tab-content");
46339         container.appendChild(body);
46340         return body;
46341     },
46342     createItemBody :function(bodyEl, id){
46343         var body = Roo.getDom(id);
46344         if(!body){
46345             body = document.createElement("div");
46346             body.id = id;
46347         }
46348         //Roo.fly(body).addClass("x-tabs-item-body");
46349         Roo.fly(body).addClass("tab-pane");
46350          bodyEl.insertBefore(body, bodyEl.firstChild);
46351         return body;
46352     },
46353     /** @private */
46354     createStripElements :  function(stripEl, text, closable, tpl)
46355     {
46356         var td = document.createElement("li"); // was td..
46357         td.className = 'nav-item';
46358         
46359         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46360         
46361         
46362         stripEl.appendChild(td);
46363         /*if(closable){
46364             td.className = "x-tabs-closable";
46365             if(!this.closeTpl){
46366                 this.closeTpl = new Roo.Template(
46367                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46368                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46369                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
46370                 );
46371             }
46372             var el = this.closeTpl.overwrite(td, {"text": text});
46373             var close = el.getElementsByTagName("div")[0];
46374             var inner = el.getElementsByTagName("em")[0];
46375             return {"el": el, "close": close, "inner": inner};
46376         } else {
46377         */
46378         // not sure what this is..
46379 //            if(!this.tabTpl){
46380                 //this.tabTpl = new Roo.Template(
46381                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46382                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46383                 //);
46384 //                this.tabTpl = new Roo.Template(
46385 //                   '<a href="#">' +
46386 //                   '<span unselectable="on"' +
46387 //                            (this.disableTooltips ? '' : ' title="{text}"') +
46388 //                            ' >{text}</span></a>'
46389 //                );
46390 //                
46391 //            }
46392
46393
46394             var template = tpl || this.tabTpl || false;
46395             
46396             if(!template){
46397                 template =  new Roo.Template(
46398                         Roo.bootstrap.version == 4 ? 
46399                             (
46400                                 '<a class="nav-link" href="#" unselectable="on"' +
46401                                      (this.disableTooltips ? '' : ' title="{text}"') +
46402                                      ' >{text}</a>'
46403                             ) : (
46404                                 '<a class="nav-link" href="#">' +
46405                                 '<span unselectable="on"' +
46406                                          (this.disableTooltips ? '' : ' title="{text}"') +
46407                                     ' >{text}</span></a>'
46408                             )
46409                 );
46410             }
46411             
46412             switch (typeof(template)) {
46413                 case 'object' :
46414                     break;
46415                 case 'string' :
46416                     template = new Roo.Template(template);
46417                     break;
46418                 default :
46419                     break;
46420             }
46421             
46422             var el = template.overwrite(td, {"text": text});
46423             
46424             var inner = el.getElementsByTagName("span")[0];
46425             
46426             return {"el": el, "inner": inner};
46427             
46428     }
46429         
46430     
46431 });
46432
46433 /**
46434  * @class Roo.TabPanelItem
46435  * @extends Roo.util.Observable
46436  * Represents an individual item (tab plus body) in a TabPanel.
46437  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46438  * @param {String} id The id of this TabPanelItem
46439  * @param {String} text The text for the tab of this TabPanelItem
46440  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46441  */
46442 Roo.bootstrap.panel.TabItem = function(config){
46443     /**
46444      * The {@link Roo.TabPanel} this TabPanelItem belongs to
46445      * @type Roo.TabPanel
46446      */
46447     this.tabPanel = config.panel;
46448     /**
46449      * The id for this TabPanelItem
46450      * @type String
46451      */
46452     this.id = config.id;
46453     /** @private */
46454     this.disabled = false;
46455     /** @private */
46456     this.text = config.text;
46457     /** @private */
46458     this.loaded = false;
46459     this.closable = config.closable;
46460
46461     /**
46462      * The body element for this TabPanelItem.
46463      * @type Roo.Element
46464      */
46465     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46466     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46467     this.bodyEl.setStyle("display", "block");
46468     this.bodyEl.setStyle("zoom", "1");
46469     //this.hideAction();
46470
46471     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46472     /** @private */
46473     this.el = Roo.get(els.el);
46474     this.inner = Roo.get(els.inner, true);
46475      this.textEl = Roo.bootstrap.version == 4 ?
46476         this.el : Roo.get(this.el.dom.firstChild, true);
46477
46478     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46479     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46480
46481     
46482 //    this.el.on("mousedown", this.onTabMouseDown, this);
46483     this.el.on("click", this.onTabClick, this);
46484     /** @private */
46485     if(config.closable){
46486         var c = Roo.get(els.close, true);
46487         c.dom.title = this.closeText;
46488         c.addClassOnOver("close-over");
46489         c.on("click", this.closeClick, this);
46490      }
46491
46492     this.addEvents({
46493          /**
46494          * @event activate
46495          * Fires when this tab becomes the active tab.
46496          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46497          * @param {Roo.TabPanelItem} this
46498          */
46499         "activate": true,
46500         /**
46501          * @event beforeclose
46502          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46503          * @param {Roo.TabPanelItem} this
46504          * @param {Object} e Set cancel to true on this object to cancel the close.
46505          */
46506         "beforeclose": true,
46507         /**
46508          * @event close
46509          * Fires when this tab is closed.
46510          * @param {Roo.TabPanelItem} this
46511          */
46512          "close": true,
46513         /**
46514          * @event deactivate
46515          * Fires when this tab is no longer the active tab.
46516          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46517          * @param {Roo.TabPanelItem} this
46518          */
46519          "deactivate" : true
46520     });
46521     this.hidden = false;
46522
46523     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46524 };
46525
46526 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46527            {
46528     purgeListeners : function(){
46529        Roo.util.Observable.prototype.purgeListeners.call(this);
46530        this.el.removeAllListeners();
46531     },
46532     /**
46533      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46534      */
46535     show : function(){
46536         this.status_node.addClass("active");
46537         this.showAction();
46538         if(Roo.isOpera){
46539             this.tabPanel.stripWrap.repaint();
46540         }
46541         this.fireEvent("activate", this.tabPanel, this);
46542     },
46543
46544     /**
46545      * Returns true if this tab is the active tab.
46546      * @return {Boolean}
46547      */
46548     isActive : function(){
46549         return this.tabPanel.getActiveTab() == this;
46550     },
46551
46552     /**
46553      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46554      */
46555     hide : function(){
46556         this.status_node.removeClass("active");
46557         this.hideAction();
46558         this.fireEvent("deactivate", this.tabPanel, this);
46559     },
46560
46561     hideAction : function(){
46562         this.bodyEl.hide();
46563         this.bodyEl.setStyle("position", "absolute");
46564         this.bodyEl.setLeft("-20000px");
46565         this.bodyEl.setTop("-20000px");
46566     },
46567
46568     showAction : function(){
46569         this.bodyEl.setStyle("position", "relative");
46570         this.bodyEl.setTop("");
46571         this.bodyEl.setLeft("");
46572         this.bodyEl.show();
46573     },
46574
46575     /**
46576      * Set the tooltip for the tab.
46577      * @param {String} tooltip The tab's tooltip
46578      */
46579     setTooltip : function(text){
46580         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46581             this.textEl.dom.qtip = text;
46582             this.textEl.dom.removeAttribute('title');
46583         }else{
46584             this.textEl.dom.title = text;
46585         }
46586     },
46587
46588     onTabClick : function(e){
46589         e.preventDefault();
46590         this.tabPanel.activate(this.id);
46591     },
46592
46593     onTabMouseDown : function(e){
46594         e.preventDefault();
46595         this.tabPanel.activate(this.id);
46596     },
46597 /*
46598     getWidth : function(){
46599         return this.inner.getWidth();
46600     },
46601
46602     setWidth : function(width){
46603         var iwidth = width - this.linode.getPadding("lr");
46604         this.inner.setWidth(iwidth);
46605         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46606         this.linode.setWidth(width);
46607     },
46608 */
46609     /**
46610      * Show or hide the tab
46611      * @param {Boolean} hidden True to hide or false to show.
46612      */
46613     setHidden : function(hidden){
46614         this.hidden = hidden;
46615         this.linode.setStyle("display", hidden ? "none" : "");
46616     },
46617
46618     /**
46619      * Returns true if this tab is "hidden"
46620      * @return {Boolean}
46621      */
46622     isHidden : function(){
46623         return this.hidden;
46624     },
46625
46626     /**
46627      * Returns the text for this tab
46628      * @return {String}
46629      */
46630     getText : function(){
46631         return this.text;
46632     },
46633     /*
46634     autoSize : function(){
46635         //this.el.beginMeasure();
46636         this.textEl.setWidth(1);
46637         /*
46638          *  #2804 [new] Tabs in Roojs
46639          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46640          */
46641         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46642         //this.el.endMeasure();
46643     //},
46644
46645     /**
46646      * Sets the text for the tab (Note: this also sets the tooltip text)
46647      * @param {String} text The tab's text and tooltip
46648      */
46649     setText : function(text){
46650         this.text = text;
46651         this.textEl.update(text);
46652         this.setTooltip(text);
46653         //if(!this.tabPanel.resizeTabs){
46654         //    this.autoSize();
46655         //}
46656     },
46657     /**
46658      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46659      */
46660     activate : function(){
46661         this.tabPanel.activate(this.id);
46662     },
46663
46664     /**
46665      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46666      */
46667     disable : function(){
46668         if(this.tabPanel.active != this){
46669             this.disabled = true;
46670             this.status_node.addClass("disabled");
46671         }
46672     },
46673
46674     /**
46675      * Enables this TabPanelItem if it was previously disabled.
46676      */
46677     enable : function(){
46678         this.disabled = false;
46679         this.status_node.removeClass("disabled");
46680     },
46681
46682     /**
46683      * Sets the content for this TabPanelItem.
46684      * @param {String} content The content
46685      * @param {Boolean} loadScripts true to look for and load scripts
46686      */
46687     setContent : function(content, loadScripts){
46688         this.bodyEl.update(content, loadScripts);
46689     },
46690
46691     /**
46692      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46693      * @return {Roo.UpdateManager} The UpdateManager
46694      */
46695     getUpdateManager : function(){
46696         return this.bodyEl.getUpdateManager();
46697     },
46698
46699     /**
46700      * Set a URL to be used to load the content for this TabPanelItem.
46701      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46702      * @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)
46703      * @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)
46704      * @return {Roo.UpdateManager} The UpdateManager
46705      */
46706     setUrl : function(url, params, loadOnce){
46707         if(this.refreshDelegate){
46708             this.un('activate', this.refreshDelegate);
46709         }
46710         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46711         this.on("activate", this.refreshDelegate);
46712         return this.bodyEl.getUpdateManager();
46713     },
46714
46715     /** @private */
46716     _handleRefresh : function(url, params, loadOnce){
46717         if(!loadOnce || !this.loaded){
46718             var updater = this.bodyEl.getUpdateManager();
46719             updater.update(url, params, this._setLoaded.createDelegate(this));
46720         }
46721     },
46722
46723     /**
46724      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
46725      *   Will fail silently if the setUrl method has not been called.
46726      *   This does not activate the panel, just updates its content.
46727      */
46728     refresh : function(){
46729         if(this.refreshDelegate){
46730            this.loaded = false;
46731            this.refreshDelegate();
46732         }
46733     },
46734
46735     /** @private */
46736     _setLoaded : function(){
46737         this.loaded = true;
46738     },
46739
46740     /** @private */
46741     closeClick : function(e){
46742         var o = {};
46743         e.stopEvent();
46744         this.fireEvent("beforeclose", this, o);
46745         if(o.cancel !== true){
46746             this.tabPanel.removeTab(this.id);
46747         }
46748     },
46749     /**
46750      * The text displayed in the tooltip for the close icon.
46751      * @type String
46752      */
46753     closeText : "Close this tab"
46754 });
46755 /**
46756 *    This script refer to:
46757 *    Title: International Telephone Input
46758 *    Author: Jack O'Connor
46759 *    Code version:  v12.1.12
46760 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46761 **/
46762
46763 Roo.bootstrap.form.PhoneInputData = function() {
46764     var d = [
46765       [
46766         "Afghanistan (‫افغانستان‬‎)",
46767         "af",
46768         "93"
46769       ],
46770       [
46771         "Albania (Shqipëri)",
46772         "al",
46773         "355"
46774       ],
46775       [
46776         "Algeria (‫الجزائر‬‎)",
46777         "dz",
46778         "213"
46779       ],
46780       [
46781         "American Samoa",
46782         "as",
46783         "1684"
46784       ],
46785       [
46786         "Andorra",
46787         "ad",
46788         "376"
46789       ],
46790       [
46791         "Angola",
46792         "ao",
46793         "244"
46794       ],
46795       [
46796         "Anguilla",
46797         "ai",
46798         "1264"
46799       ],
46800       [
46801         "Antigua and Barbuda",
46802         "ag",
46803         "1268"
46804       ],
46805       [
46806         "Argentina",
46807         "ar",
46808         "54"
46809       ],
46810       [
46811         "Armenia (Հայաստան)",
46812         "am",
46813         "374"
46814       ],
46815       [
46816         "Aruba",
46817         "aw",
46818         "297"
46819       ],
46820       [
46821         "Australia",
46822         "au",
46823         "61",
46824         0
46825       ],
46826       [
46827         "Austria (Österreich)",
46828         "at",
46829         "43"
46830       ],
46831       [
46832         "Azerbaijan (Azərbaycan)",
46833         "az",
46834         "994"
46835       ],
46836       [
46837         "Bahamas",
46838         "bs",
46839         "1242"
46840       ],
46841       [
46842         "Bahrain (‫البحرين‬‎)",
46843         "bh",
46844         "973"
46845       ],
46846       [
46847         "Bangladesh (বাংলাদেশ)",
46848         "bd",
46849         "880"
46850       ],
46851       [
46852         "Barbados",
46853         "bb",
46854         "1246"
46855       ],
46856       [
46857         "Belarus (Беларусь)",
46858         "by",
46859         "375"
46860       ],
46861       [
46862         "Belgium (België)",
46863         "be",
46864         "32"
46865       ],
46866       [
46867         "Belize",
46868         "bz",
46869         "501"
46870       ],
46871       [
46872         "Benin (Bénin)",
46873         "bj",
46874         "229"
46875       ],
46876       [
46877         "Bermuda",
46878         "bm",
46879         "1441"
46880       ],
46881       [
46882         "Bhutan (འབྲུག)",
46883         "bt",
46884         "975"
46885       ],
46886       [
46887         "Bolivia",
46888         "bo",
46889         "591"
46890       ],
46891       [
46892         "Bosnia and Herzegovina (Босна и Херцеговина)",
46893         "ba",
46894         "387"
46895       ],
46896       [
46897         "Botswana",
46898         "bw",
46899         "267"
46900       ],
46901       [
46902         "Brazil (Brasil)",
46903         "br",
46904         "55"
46905       ],
46906       [
46907         "British Indian Ocean Territory",
46908         "io",
46909         "246"
46910       ],
46911       [
46912         "British Virgin Islands",
46913         "vg",
46914         "1284"
46915       ],
46916       [
46917         "Brunei",
46918         "bn",
46919         "673"
46920       ],
46921       [
46922         "Bulgaria (България)",
46923         "bg",
46924         "359"
46925       ],
46926       [
46927         "Burkina Faso",
46928         "bf",
46929         "226"
46930       ],
46931       [
46932         "Burundi (Uburundi)",
46933         "bi",
46934         "257"
46935       ],
46936       [
46937         "Cambodia (កម្ពុជា)",
46938         "kh",
46939         "855"
46940       ],
46941       [
46942         "Cameroon (Cameroun)",
46943         "cm",
46944         "237"
46945       ],
46946       [
46947         "Canada",
46948         "ca",
46949         "1",
46950         1,
46951         ["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"]
46952       ],
46953       [
46954         "Cape Verde (Kabu Verdi)",
46955         "cv",
46956         "238"
46957       ],
46958       [
46959         "Caribbean Netherlands",
46960         "bq",
46961         "599",
46962         1
46963       ],
46964       [
46965         "Cayman Islands",
46966         "ky",
46967         "1345"
46968       ],
46969       [
46970         "Central African Republic (République centrafricaine)",
46971         "cf",
46972         "236"
46973       ],
46974       [
46975         "Chad (Tchad)",
46976         "td",
46977         "235"
46978       ],
46979       [
46980         "Chile",
46981         "cl",
46982         "56"
46983       ],
46984       [
46985         "China (中国)",
46986         "cn",
46987         "86"
46988       ],
46989       [
46990         "Christmas Island",
46991         "cx",
46992         "61",
46993         2
46994       ],
46995       [
46996         "Cocos (Keeling) Islands",
46997         "cc",
46998         "61",
46999         1
47000       ],
47001       [
47002         "Colombia",
47003         "co",
47004         "57"
47005       ],
47006       [
47007         "Comoros (‫جزر القمر‬‎)",
47008         "km",
47009         "269"
47010       ],
47011       [
47012         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
47013         "cd",
47014         "243"
47015       ],
47016       [
47017         "Congo (Republic) (Congo-Brazzaville)",
47018         "cg",
47019         "242"
47020       ],
47021       [
47022         "Cook Islands",
47023         "ck",
47024         "682"
47025       ],
47026       [
47027         "Costa Rica",
47028         "cr",
47029         "506"
47030       ],
47031       [
47032         "Côte d’Ivoire",
47033         "ci",
47034         "225"
47035       ],
47036       [
47037         "Croatia (Hrvatska)",
47038         "hr",
47039         "385"
47040       ],
47041       [
47042         "Cuba",
47043         "cu",
47044         "53"
47045       ],
47046       [
47047         "Curaçao",
47048         "cw",
47049         "599",
47050         0
47051       ],
47052       [
47053         "Cyprus (Κύπρος)",
47054         "cy",
47055         "357"
47056       ],
47057       [
47058         "Czech Republic (Česká republika)",
47059         "cz",
47060         "420"
47061       ],
47062       [
47063         "Denmark (Danmark)",
47064         "dk",
47065         "45"
47066       ],
47067       [
47068         "Djibouti",
47069         "dj",
47070         "253"
47071       ],
47072       [
47073         "Dominica",
47074         "dm",
47075         "1767"
47076       ],
47077       [
47078         "Dominican Republic (República Dominicana)",
47079         "do",
47080         "1",
47081         2,
47082         ["809", "829", "849"]
47083       ],
47084       [
47085         "Ecuador",
47086         "ec",
47087         "593"
47088       ],
47089       [
47090         "Egypt (‫مصر‬‎)",
47091         "eg",
47092         "20"
47093       ],
47094       [
47095         "El Salvador",
47096         "sv",
47097         "503"
47098       ],
47099       [
47100         "Equatorial Guinea (Guinea Ecuatorial)",
47101         "gq",
47102         "240"
47103       ],
47104       [
47105         "Eritrea",
47106         "er",
47107         "291"
47108       ],
47109       [
47110         "Estonia (Eesti)",
47111         "ee",
47112         "372"
47113       ],
47114       [
47115         "Ethiopia",
47116         "et",
47117         "251"
47118       ],
47119       [
47120         "Falkland Islands (Islas Malvinas)",
47121         "fk",
47122         "500"
47123       ],
47124       [
47125         "Faroe Islands (Føroyar)",
47126         "fo",
47127         "298"
47128       ],
47129       [
47130         "Fiji",
47131         "fj",
47132         "679"
47133       ],
47134       [
47135         "Finland (Suomi)",
47136         "fi",
47137         "358",
47138         0
47139       ],
47140       [
47141         "France",
47142         "fr",
47143         "33"
47144       ],
47145       [
47146         "French Guiana (Guyane française)",
47147         "gf",
47148         "594"
47149       ],
47150       [
47151         "French Polynesia (Polynésie française)",
47152         "pf",
47153         "689"
47154       ],
47155       [
47156         "Gabon",
47157         "ga",
47158         "241"
47159       ],
47160       [
47161         "Gambia",
47162         "gm",
47163         "220"
47164       ],
47165       [
47166         "Georgia (საქართველო)",
47167         "ge",
47168         "995"
47169       ],
47170       [
47171         "Germany (Deutschland)",
47172         "de",
47173         "49"
47174       ],
47175       [
47176         "Ghana (Gaana)",
47177         "gh",
47178         "233"
47179       ],
47180       [
47181         "Gibraltar",
47182         "gi",
47183         "350"
47184       ],
47185       [
47186         "Greece (Ελλάδα)",
47187         "gr",
47188         "30"
47189       ],
47190       [
47191         "Greenland (Kalaallit Nunaat)",
47192         "gl",
47193         "299"
47194       ],
47195       [
47196         "Grenada",
47197         "gd",
47198         "1473"
47199       ],
47200       [
47201         "Guadeloupe",
47202         "gp",
47203         "590",
47204         0
47205       ],
47206       [
47207         "Guam",
47208         "gu",
47209         "1671"
47210       ],
47211       [
47212         "Guatemala",
47213         "gt",
47214         "502"
47215       ],
47216       [
47217         "Guernsey",
47218         "gg",
47219         "44",
47220         1
47221       ],
47222       [
47223         "Guinea (Guinée)",
47224         "gn",
47225         "224"
47226       ],
47227       [
47228         "Guinea-Bissau (Guiné Bissau)",
47229         "gw",
47230         "245"
47231       ],
47232       [
47233         "Guyana",
47234         "gy",
47235         "592"
47236       ],
47237       [
47238         "Haiti",
47239         "ht",
47240         "509"
47241       ],
47242       [
47243         "Honduras",
47244         "hn",
47245         "504"
47246       ],
47247       [
47248         "Hong Kong (香港)",
47249         "hk",
47250         "852"
47251       ],
47252       [
47253         "Hungary (Magyarország)",
47254         "hu",
47255         "36"
47256       ],
47257       [
47258         "Iceland (Ísland)",
47259         "is",
47260         "354"
47261       ],
47262       [
47263         "India (भारत)",
47264         "in",
47265         "91"
47266       ],
47267       [
47268         "Indonesia",
47269         "id",
47270         "62"
47271       ],
47272       [
47273         "Iran (‫ایران‬‎)",
47274         "ir",
47275         "98"
47276       ],
47277       [
47278         "Iraq (‫العراق‬‎)",
47279         "iq",
47280         "964"
47281       ],
47282       [
47283         "Ireland",
47284         "ie",
47285         "353"
47286       ],
47287       [
47288         "Isle of Man",
47289         "im",
47290         "44",
47291         2
47292       ],
47293       [
47294         "Israel (‫ישראל‬‎)",
47295         "il",
47296         "972"
47297       ],
47298       [
47299         "Italy (Italia)",
47300         "it",
47301         "39",
47302         0
47303       ],
47304       [
47305         "Jamaica",
47306         "jm",
47307         "1876"
47308       ],
47309       [
47310         "Japan (日本)",
47311         "jp",
47312         "81"
47313       ],
47314       [
47315         "Jersey",
47316         "je",
47317         "44",
47318         3
47319       ],
47320       [
47321         "Jordan (‫الأردن‬‎)",
47322         "jo",
47323         "962"
47324       ],
47325       [
47326         "Kazakhstan (Казахстан)",
47327         "kz",
47328         "7",
47329         1
47330       ],
47331       [
47332         "Kenya",
47333         "ke",
47334         "254"
47335       ],
47336       [
47337         "Kiribati",
47338         "ki",
47339         "686"
47340       ],
47341       [
47342         "Kosovo",
47343         "xk",
47344         "383"
47345       ],
47346       [
47347         "Kuwait (‫الكويت‬‎)",
47348         "kw",
47349         "965"
47350       ],
47351       [
47352         "Kyrgyzstan (Кыргызстан)",
47353         "kg",
47354         "996"
47355       ],
47356       [
47357         "Laos (ລາວ)",
47358         "la",
47359         "856"
47360       ],
47361       [
47362         "Latvia (Latvija)",
47363         "lv",
47364         "371"
47365       ],
47366       [
47367         "Lebanon (‫لبنان‬‎)",
47368         "lb",
47369         "961"
47370       ],
47371       [
47372         "Lesotho",
47373         "ls",
47374         "266"
47375       ],
47376       [
47377         "Liberia",
47378         "lr",
47379         "231"
47380       ],
47381       [
47382         "Libya (‫ليبيا‬‎)",
47383         "ly",
47384         "218"
47385       ],
47386       [
47387         "Liechtenstein",
47388         "li",
47389         "423"
47390       ],
47391       [
47392         "Lithuania (Lietuva)",
47393         "lt",
47394         "370"
47395       ],
47396       [
47397         "Luxembourg",
47398         "lu",
47399         "352"
47400       ],
47401       [
47402         "Macau (澳門)",
47403         "mo",
47404         "853"
47405       ],
47406       [
47407         "Macedonia (FYROM) (Македонија)",
47408         "mk",
47409         "389"
47410       ],
47411       [
47412         "Madagascar (Madagasikara)",
47413         "mg",
47414         "261"
47415       ],
47416       [
47417         "Malawi",
47418         "mw",
47419         "265"
47420       ],
47421       [
47422         "Malaysia",
47423         "my",
47424         "60"
47425       ],
47426       [
47427         "Maldives",
47428         "mv",
47429         "960"
47430       ],
47431       [
47432         "Mali",
47433         "ml",
47434         "223"
47435       ],
47436       [
47437         "Malta",
47438         "mt",
47439         "356"
47440       ],
47441       [
47442         "Marshall Islands",
47443         "mh",
47444         "692"
47445       ],
47446       [
47447         "Martinique",
47448         "mq",
47449         "596"
47450       ],
47451       [
47452         "Mauritania (‫موريتانيا‬‎)",
47453         "mr",
47454         "222"
47455       ],
47456       [
47457         "Mauritius (Moris)",
47458         "mu",
47459         "230"
47460       ],
47461       [
47462         "Mayotte",
47463         "yt",
47464         "262",
47465         1
47466       ],
47467       [
47468         "Mexico (México)",
47469         "mx",
47470         "52"
47471       ],
47472       [
47473         "Micronesia",
47474         "fm",
47475         "691"
47476       ],
47477       [
47478         "Moldova (Republica Moldova)",
47479         "md",
47480         "373"
47481       ],
47482       [
47483         "Monaco",
47484         "mc",
47485         "377"
47486       ],
47487       [
47488         "Mongolia (Монгол)",
47489         "mn",
47490         "976"
47491       ],
47492       [
47493         "Montenegro (Crna Gora)",
47494         "me",
47495         "382"
47496       ],
47497       [
47498         "Montserrat",
47499         "ms",
47500         "1664"
47501       ],
47502       [
47503         "Morocco (‫المغرب‬‎)",
47504         "ma",
47505         "212",
47506         0
47507       ],
47508       [
47509         "Mozambique (Moçambique)",
47510         "mz",
47511         "258"
47512       ],
47513       [
47514         "Myanmar (Burma) (မြန်မာ)",
47515         "mm",
47516         "95"
47517       ],
47518       [
47519         "Namibia (Namibië)",
47520         "na",
47521         "264"
47522       ],
47523       [
47524         "Nauru",
47525         "nr",
47526         "674"
47527       ],
47528       [
47529         "Nepal (नेपाल)",
47530         "np",
47531         "977"
47532       ],
47533       [
47534         "Netherlands (Nederland)",
47535         "nl",
47536         "31"
47537       ],
47538       [
47539         "New Caledonia (Nouvelle-Calédonie)",
47540         "nc",
47541         "687"
47542       ],
47543       [
47544         "New Zealand",
47545         "nz",
47546         "64"
47547       ],
47548       [
47549         "Nicaragua",
47550         "ni",
47551         "505"
47552       ],
47553       [
47554         "Niger (Nijar)",
47555         "ne",
47556         "227"
47557       ],
47558       [
47559         "Nigeria",
47560         "ng",
47561         "234"
47562       ],
47563       [
47564         "Niue",
47565         "nu",
47566         "683"
47567       ],
47568       [
47569         "Norfolk Island",
47570         "nf",
47571         "672"
47572       ],
47573       [
47574         "North Korea (조선 민주주의 인민 공화국)",
47575         "kp",
47576         "850"
47577       ],
47578       [
47579         "Northern Mariana Islands",
47580         "mp",
47581         "1670"
47582       ],
47583       [
47584         "Norway (Norge)",
47585         "no",
47586         "47",
47587         0
47588       ],
47589       [
47590         "Oman (‫عُمان‬‎)",
47591         "om",
47592         "968"
47593       ],
47594       [
47595         "Pakistan (‫پاکستان‬‎)",
47596         "pk",
47597         "92"
47598       ],
47599       [
47600         "Palau",
47601         "pw",
47602         "680"
47603       ],
47604       [
47605         "Palestine (‫فلسطين‬‎)",
47606         "ps",
47607         "970"
47608       ],
47609       [
47610         "Panama (Panamá)",
47611         "pa",
47612         "507"
47613       ],
47614       [
47615         "Papua New Guinea",
47616         "pg",
47617         "675"
47618       ],
47619       [
47620         "Paraguay",
47621         "py",
47622         "595"
47623       ],
47624       [
47625         "Peru (Perú)",
47626         "pe",
47627         "51"
47628       ],
47629       [
47630         "Philippines",
47631         "ph",
47632         "63"
47633       ],
47634       [
47635         "Poland (Polska)",
47636         "pl",
47637         "48"
47638       ],
47639       [
47640         "Portugal",
47641         "pt",
47642         "351"
47643       ],
47644       [
47645         "Puerto Rico",
47646         "pr",
47647         "1",
47648         3,
47649         ["787", "939"]
47650       ],
47651       [
47652         "Qatar (‫قطر‬‎)",
47653         "qa",
47654         "974"
47655       ],
47656       [
47657         "Réunion (La Réunion)",
47658         "re",
47659         "262",
47660         0
47661       ],
47662       [
47663         "Romania (România)",
47664         "ro",
47665         "40"
47666       ],
47667       [
47668         "Russia (Россия)",
47669         "ru",
47670         "7",
47671         0
47672       ],
47673       [
47674         "Rwanda",
47675         "rw",
47676         "250"
47677       ],
47678       [
47679         "Saint Barthélemy",
47680         "bl",
47681         "590",
47682         1
47683       ],
47684       [
47685         "Saint Helena",
47686         "sh",
47687         "290"
47688       ],
47689       [
47690         "Saint Kitts and Nevis",
47691         "kn",
47692         "1869"
47693       ],
47694       [
47695         "Saint Lucia",
47696         "lc",
47697         "1758"
47698       ],
47699       [
47700         "Saint Martin (Saint-Martin (partie française))",
47701         "mf",
47702         "590",
47703         2
47704       ],
47705       [
47706         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47707         "pm",
47708         "508"
47709       ],
47710       [
47711         "Saint Vincent and the Grenadines",
47712         "vc",
47713         "1784"
47714       ],
47715       [
47716         "Samoa",
47717         "ws",
47718         "685"
47719       ],
47720       [
47721         "San Marino",
47722         "sm",
47723         "378"
47724       ],
47725       [
47726         "São Tomé and Príncipe (São Tomé e Príncipe)",
47727         "st",
47728         "239"
47729       ],
47730       [
47731         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
47732         "sa",
47733         "966"
47734       ],
47735       [
47736         "Senegal (Sénégal)",
47737         "sn",
47738         "221"
47739       ],
47740       [
47741         "Serbia (Србија)",
47742         "rs",
47743         "381"
47744       ],
47745       [
47746         "Seychelles",
47747         "sc",
47748         "248"
47749       ],
47750       [
47751         "Sierra Leone",
47752         "sl",
47753         "232"
47754       ],
47755       [
47756         "Singapore",
47757         "sg",
47758         "65"
47759       ],
47760       [
47761         "Sint Maarten",
47762         "sx",
47763         "1721"
47764       ],
47765       [
47766         "Slovakia (Slovensko)",
47767         "sk",
47768         "421"
47769       ],
47770       [
47771         "Slovenia (Slovenija)",
47772         "si",
47773         "386"
47774       ],
47775       [
47776         "Solomon Islands",
47777         "sb",
47778         "677"
47779       ],
47780       [
47781         "Somalia (Soomaaliya)",
47782         "so",
47783         "252"
47784       ],
47785       [
47786         "South Africa",
47787         "za",
47788         "27"
47789       ],
47790       [
47791         "South Korea (대한민국)",
47792         "kr",
47793         "82"
47794       ],
47795       [
47796         "South Sudan (‫جنوب السودان‬‎)",
47797         "ss",
47798         "211"
47799       ],
47800       [
47801         "Spain (España)",
47802         "es",
47803         "34"
47804       ],
47805       [
47806         "Sri Lanka (ශ්‍රී ලංකාව)",
47807         "lk",
47808         "94"
47809       ],
47810       [
47811         "Sudan (‫السودان‬‎)",
47812         "sd",
47813         "249"
47814       ],
47815       [
47816         "Suriname",
47817         "sr",
47818         "597"
47819       ],
47820       [
47821         "Svalbard and Jan Mayen",
47822         "sj",
47823         "47",
47824         1
47825       ],
47826       [
47827         "Swaziland",
47828         "sz",
47829         "268"
47830       ],
47831       [
47832         "Sweden (Sverige)",
47833         "se",
47834         "46"
47835       ],
47836       [
47837         "Switzerland (Schweiz)",
47838         "ch",
47839         "41"
47840       ],
47841       [
47842         "Syria (‫سوريا‬‎)",
47843         "sy",
47844         "963"
47845       ],
47846       [
47847         "Taiwan (台灣)",
47848         "tw",
47849         "886"
47850       ],
47851       [
47852         "Tajikistan",
47853         "tj",
47854         "992"
47855       ],
47856       [
47857         "Tanzania",
47858         "tz",
47859         "255"
47860       ],
47861       [
47862         "Thailand (ไทย)",
47863         "th",
47864         "66"
47865       ],
47866       [
47867         "Timor-Leste",
47868         "tl",
47869         "670"
47870       ],
47871       [
47872         "Togo",
47873         "tg",
47874         "228"
47875       ],
47876       [
47877         "Tokelau",
47878         "tk",
47879         "690"
47880       ],
47881       [
47882         "Tonga",
47883         "to",
47884         "676"
47885       ],
47886       [
47887         "Trinidad and Tobago",
47888         "tt",
47889         "1868"
47890       ],
47891       [
47892         "Tunisia (‫تونس‬‎)",
47893         "tn",
47894         "216"
47895       ],
47896       [
47897         "Turkey (Türkiye)",
47898         "tr",
47899         "90"
47900       ],
47901       [
47902         "Turkmenistan",
47903         "tm",
47904         "993"
47905       ],
47906       [
47907         "Turks and Caicos Islands",
47908         "tc",
47909         "1649"
47910       ],
47911       [
47912         "Tuvalu",
47913         "tv",
47914         "688"
47915       ],
47916       [
47917         "U.S. Virgin Islands",
47918         "vi",
47919         "1340"
47920       ],
47921       [
47922         "Uganda",
47923         "ug",
47924         "256"
47925       ],
47926       [
47927         "Ukraine (Україна)",
47928         "ua",
47929         "380"
47930       ],
47931       [
47932         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
47933         "ae",
47934         "971"
47935       ],
47936       [
47937         "United Kingdom",
47938         "gb",
47939         "44",
47940         0
47941       ],
47942       [
47943         "United States",
47944         "us",
47945         "1",
47946         0
47947       ],
47948       [
47949         "Uruguay",
47950         "uy",
47951         "598"
47952       ],
47953       [
47954         "Uzbekistan (Oʻzbekiston)",
47955         "uz",
47956         "998"
47957       ],
47958       [
47959         "Vanuatu",
47960         "vu",
47961         "678"
47962       ],
47963       [
47964         "Vatican City (Città del Vaticano)",
47965         "va",
47966         "39",
47967         1
47968       ],
47969       [
47970         "Venezuela",
47971         "ve",
47972         "58"
47973       ],
47974       [
47975         "Vietnam (Việt Nam)",
47976         "vn",
47977         "84"
47978       ],
47979       [
47980         "Wallis and Futuna (Wallis-et-Futuna)",
47981         "wf",
47982         "681"
47983       ],
47984       [
47985         "Western Sahara (‫الصحراء الغربية‬‎)",
47986         "eh",
47987         "212",
47988         1
47989       ],
47990       [
47991         "Yemen (‫اليمن‬‎)",
47992         "ye",
47993         "967"
47994       ],
47995       [
47996         "Zambia",
47997         "zm",
47998         "260"
47999       ],
48000       [
48001         "Zimbabwe",
48002         "zw",
48003         "263"
48004       ],
48005       [
48006         "Åland Islands",
48007         "ax",
48008         "358",
48009         1
48010       ]
48011   ];
48012   
48013   return d;
48014 }/**
48015 *    This script refer to:
48016 *    Title: International Telephone Input
48017 *    Author: Jack O'Connor
48018 *    Code version:  v12.1.12
48019 *    Availability: https://github.com/jackocnr/intl-tel-input.git
48020 **/
48021
48022 /**
48023  * @class Roo.bootstrap.form.PhoneInput
48024  * @extends Roo.bootstrap.form.TriggerField
48025  * An input with International dial-code selection
48026  
48027  * @cfg {String} defaultDialCode default '+852'
48028  * @cfg {Array} preferedCountries default []
48029   
48030  * @constructor
48031  * Create a new PhoneInput.
48032  * @param {Object} config Configuration options
48033  */
48034
48035 Roo.bootstrap.form.PhoneInput = function(config) {
48036     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48037 };
48038
48039 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48040         /**
48041         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48042         */
48043         listWidth: undefined,
48044         
48045         selectedClass: 'active',
48046         
48047         invalidClass : "has-warning",
48048         
48049         validClass: 'has-success',
48050         
48051         allowed: '0123456789',
48052         
48053         max_length: 15,
48054         
48055         /**
48056          * @cfg {String} defaultDialCode The default dial code when initializing the input
48057          */
48058         defaultDialCode: '+852',
48059         
48060         /**
48061          * @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
48062          */
48063         preferedCountries: false,
48064         
48065         getAutoCreate : function()
48066         {
48067             var data = Roo.bootstrap.form.PhoneInputData();
48068             var align = this.labelAlign || this.parentLabelAlign();
48069             var id = Roo.id();
48070             
48071             this.allCountries = [];
48072             this.dialCodeMapping = [];
48073             
48074             for (var i = 0; i < data.length; i++) {
48075               var c = data[i];
48076               this.allCountries[i] = {
48077                 name: c[0],
48078                 iso2: c[1],
48079                 dialCode: c[2],
48080                 priority: c[3] || 0,
48081                 areaCodes: c[4] || null
48082               };
48083               this.dialCodeMapping[c[2]] = {
48084                   name: c[0],
48085                   iso2: c[1],
48086                   priority: c[3] || 0,
48087                   areaCodes: c[4] || null
48088               };
48089             }
48090             
48091             var cfg = {
48092                 cls: 'form-group',
48093                 cn: []
48094             };
48095             
48096             var input =  {
48097                 tag: 'input',
48098                 id : id,
48099                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48100                 maxlength: this.max_length,
48101                 cls : 'form-control tel-input',
48102                 autocomplete: 'new-password'
48103             };
48104             
48105             var hiddenInput = {
48106                 tag: 'input',
48107                 type: 'hidden',
48108                 cls: 'hidden-tel-input'
48109             };
48110             
48111             if (this.name) {
48112                 hiddenInput.name = this.name;
48113             }
48114             
48115             if (this.disabled) {
48116                 input.disabled = true;
48117             }
48118             
48119             var flag_container = {
48120                 tag: 'div',
48121                 cls: 'flag-box',
48122                 cn: [
48123                     {
48124                         tag: 'div',
48125                         cls: 'flag'
48126                     },
48127                     {
48128                         tag: 'div',
48129                         cls: 'caret'
48130                     }
48131                 ]
48132             };
48133             
48134             var box = {
48135                 tag: 'div',
48136                 cls: this.hasFeedback ? 'has-feedback' : '',
48137                 cn: [
48138                     hiddenInput,
48139                     input,
48140                     {
48141                         tag: 'input',
48142                         cls: 'dial-code-holder',
48143                         disabled: true
48144                     }
48145                 ]
48146             };
48147             
48148             var container = {
48149                 cls: 'roo-select2-container input-group',
48150                 cn: [
48151                     flag_container,
48152                     box
48153                 ]
48154             };
48155             
48156             if (this.fieldLabel.length) {
48157                 var indicator = {
48158                     tag: 'i',
48159                     tooltip: 'This field is required'
48160                 };
48161                 
48162                 var label = {
48163                     tag: 'label',
48164                     'for':  id,
48165                     cls: 'control-label',
48166                     cn: []
48167                 };
48168                 
48169                 var label_text = {
48170                     tag: 'span',
48171                     html: this.fieldLabel
48172                 };
48173                 
48174                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48175                 label.cn = [
48176                     indicator,
48177                     label_text
48178                 ];
48179                 
48180                 if(this.indicatorpos == 'right') {
48181                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48182                     label.cn = [
48183                         label_text,
48184                         indicator
48185                     ];
48186                 }
48187                 
48188                 if(align == 'left') {
48189                     container = {
48190                         tag: 'div',
48191                         cn: [
48192                             container
48193                         ]
48194                     };
48195                     
48196                     if(this.labelWidth > 12){
48197                         label.style = "width: " + this.labelWidth + 'px';
48198                     }
48199                     if(this.labelWidth < 13 && this.labelmd == 0){
48200                         this.labelmd = this.labelWidth;
48201                     }
48202                     if(this.labellg > 0){
48203                         label.cls += ' col-lg-' + this.labellg;
48204                         input.cls += ' col-lg-' + (12 - this.labellg);
48205                     }
48206                     if(this.labelmd > 0){
48207                         label.cls += ' col-md-' + this.labelmd;
48208                         container.cls += ' col-md-' + (12 - this.labelmd);
48209                     }
48210                     if(this.labelsm > 0){
48211                         label.cls += ' col-sm-' + this.labelsm;
48212                         container.cls += ' col-sm-' + (12 - this.labelsm);
48213                     }
48214                     if(this.labelxs > 0){
48215                         label.cls += ' col-xs-' + this.labelxs;
48216                         container.cls += ' col-xs-' + (12 - this.labelxs);
48217                     }
48218                 }
48219             }
48220             
48221             cfg.cn = [
48222                 label,
48223                 container
48224             ];
48225             
48226             var settings = this;
48227             
48228             ['xs','sm','md','lg'].map(function(size){
48229                 if (settings[size]) {
48230                     cfg.cls += ' col-' + size + '-' + settings[size];
48231                 }
48232             });
48233             
48234             this.store = new Roo.data.Store({
48235                 proxy : new Roo.data.MemoryProxy({}),
48236                 reader : new Roo.data.JsonReader({
48237                     fields : [
48238                         {
48239                             'name' : 'name',
48240                             'type' : 'string'
48241                         },
48242                         {
48243                             'name' : 'iso2',
48244                             'type' : 'string'
48245                         },
48246                         {
48247                             'name' : 'dialCode',
48248                             'type' : 'string'
48249                         },
48250                         {
48251                             'name' : 'priority',
48252                             'type' : 'string'
48253                         },
48254                         {
48255                             'name' : 'areaCodes',
48256                             'type' : 'string'
48257                         }
48258                     ]
48259                 })
48260             });
48261             
48262             if(!this.preferedCountries) {
48263                 this.preferedCountries = [
48264                     'hk',
48265                     'gb',
48266                     'us'
48267                 ];
48268             }
48269             
48270             var p = this.preferedCountries.reverse();
48271             
48272             if(p) {
48273                 for (var i = 0; i < p.length; i++) {
48274                     for (var j = 0; j < this.allCountries.length; j++) {
48275                         if(this.allCountries[j].iso2 == p[i]) {
48276                             var t = this.allCountries[j];
48277                             this.allCountries.splice(j,1);
48278                             this.allCountries.unshift(t);
48279                         }
48280                     } 
48281                 }
48282             }
48283             
48284             this.store.proxy.data = {
48285                 success: true,
48286                 data: this.allCountries
48287             };
48288             
48289             return cfg;
48290         },
48291         
48292         initEvents : function()
48293         {
48294             this.createList();
48295             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48296             
48297             this.indicator = this.indicatorEl();
48298             this.flag = this.flagEl();
48299             this.dialCodeHolder = this.dialCodeHolderEl();
48300             
48301             this.trigger = this.el.select('div.flag-box',true).first();
48302             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48303             
48304             var _this = this;
48305             
48306             (function(){
48307                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48308                 _this.list.setWidth(lw);
48309             }).defer(100);
48310             
48311             this.list.on('mouseover', this.onViewOver, this);
48312             this.list.on('mousemove', this.onViewMove, this);
48313             this.inputEl().on("keyup", this.onKeyUp, this);
48314             this.inputEl().on("keypress", this.onKeyPress, this);
48315             
48316             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48317
48318             this.view = new Roo.View(this.list, this.tpl, {
48319                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48320             });
48321             
48322             this.view.on('click', this.onViewClick, this);
48323             this.setValue(this.defaultDialCode);
48324         },
48325         
48326         onTriggerClick : function(e)
48327         {
48328             Roo.log('trigger click');
48329             if(this.disabled){
48330                 return;
48331             }
48332             
48333             if(this.isExpanded()){
48334                 this.collapse();
48335                 this.hasFocus = false;
48336             }else {
48337                 this.store.load({});
48338                 this.hasFocus = true;
48339                 this.expand();
48340             }
48341         },
48342         
48343         isExpanded : function()
48344         {
48345             return this.list.isVisible();
48346         },
48347         
48348         collapse : function()
48349         {
48350             if(!this.isExpanded()){
48351                 return;
48352             }
48353             this.list.hide();
48354             Roo.get(document).un('mousedown', this.collapseIf, this);
48355             Roo.get(document).un('mousewheel', this.collapseIf, this);
48356             this.fireEvent('collapse', this);
48357             this.validate();
48358         },
48359         
48360         expand : function()
48361         {
48362             Roo.log('expand');
48363
48364             if(this.isExpanded() || !this.hasFocus){
48365                 return;
48366             }
48367             
48368             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48369             this.list.setWidth(lw);
48370             
48371             this.list.show();
48372             this.restrictHeight();
48373             
48374             Roo.get(document).on('mousedown', this.collapseIf, this);
48375             Roo.get(document).on('mousewheel', this.collapseIf, this);
48376             
48377             this.fireEvent('expand', this);
48378         },
48379         
48380         restrictHeight : function()
48381         {
48382             this.list.alignTo(this.inputEl(), this.listAlign);
48383             this.list.alignTo(this.inputEl(), this.listAlign);
48384         },
48385         
48386         onViewOver : function(e, t)
48387         {
48388             if(this.inKeyMode){
48389                 return;
48390             }
48391             var item = this.view.findItemFromChild(t);
48392             
48393             if(item){
48394                 var index = this.view.indexOf(item);
48395                 this.select(index, false);
48396             }
48397         },
48398
48399         // private
48400         onViewClick : function(view, doFocus, el, e)
48401         {
48402             var index = this.view.getSelectedIndexes()[0];
48403             
48404             var r = this.store.getAt(index);
48405             
48406             if(r){
48407                 this.onSelect(r, index);
48408             }
48409             if(doFocus !== false && !this.blockFocus){
48410                 this.inputEl().focus();
48411             }
48412         },
48413         
48414         onViewMove : function(e, t)
48415         {
48416             this.inKeyMode = false;
48417         },
48418         
48419         select : function(index, scrollIntoView)
48420         {
48421             this.selectedIndex = index;
48422             this.view.select(index);
48423             if(scrollIntoView !== false){
48424                 var el = this.view.getNode(index);
48425                 if(el){
48426                     this.list.scrollChildIntoView(el, false);
48427                 }
48428             }
48429         },
48430         
48431         createList : function()
48432         {
48433             this.list = Roo.get(document.body).createChild({
48434                 tag: 'ul',
48435                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48436                 style: 'display:none'
48437             });
48438             
48439             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48440         },
48441         
48442         collapseIf : function(e)
48443         {
48444             var in_combo  = e.within(this.el);
48445             var in_list =  e.within(this.list);
48446             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48447             
48448             if (in_combo || in_list || is_list) {
48449                 return;
48450             }
48451             this.collapse();
48452         },
48453         
48454         onSelect : function(record, index)
48455         {
48456             if(this.fireEvent('beforeselect', this, record, index) !== false){
48457                 
48458                 this.setFlagClass(record.data.iso2);
48459                 this.setDialCode(record.data.dialCode);
48460                 this.hasFocus = false;
48461                 this.collapse();
48462                 this.fireEvent('select', this, record, index);
48463             }
48464         },
48465         
48466         flagEl : function()
48467         {
48468             var flag = this.el.select('div.flag',true).first();
48469             if(!flag){
48470                 return false;
48471             }
48472             return flag;
48473         },
48474         
48475         dialCodeHolderEl : function()
48476         {
48477             var d = this.el.select('input.dial-code-holder',true).first();
48478             if(!d){
48479                 return false;
48480             }
48481             return d;
48482         },
48483         
48484         setDialCode : function(v)
48485         {
48486             this.dialCodeHolder.dom.value = '+'+v;
48487         },
48488         
48489         setFlagClass : function(n)
48490         {
48491             this.flag.dom.className = 'flag '+n;
48492         },
48493         
48494         getValue : function()
48495         {
48496             var v = this.inputEl().getValue();
48497             if(this.dialCodeHolder) {
48498                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48499             }
48500             return v;
48501         },
48502         
48503         setValue : function(v)
48504         {
48505             var d = this.getDialCode(v);
48506             
48507             //invalid dial code
48508             if(v.length == 0 || !d || d.length == 0) {
48509                 if(this.rendered){
48510                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48511                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48512                 }
48513                 return;
48514             }
48515             
48516             //valid dial code
48517             this.setFlagClass(this.dialCodeMapping[d].iso2);
48518             this.setDialCode(d);
48519             this.inputEl().dom.value = v.replace('+'+d,'');
48520             this.hiddenEl().dom.value = this.getValue();
48521             
48522             this.validate();
48523         },
48524         
48525         getDialCode : function(v)
48526         {
48527             v = v ||  '';
48528             
48529             if (v.length == 0) {
48530                 return this.dialCodeHolder.dom.value;
48531             }
48532             
48533             var dialCode = "";
48534             if (v.charAt(0) != "+") {
48535                 return false;
48536             }
48537             var numericChars = "";
48538             for (var i = 1; i < v.length; i++) {
48539               var c = v.charAt(i);
48540               if (!isNaN(c)) {
48541                 numericChars += c;
48542                 if (this.dialCodeMapping[numericChars]) {
48543                   dialCode = v.substr(1, i);
48544                 }
48545                 if (numericChars.length == 4) {
48546                   break;
48547                 }
48548               }
48549             }
48550             return dialCode;
48551         },
48552         
48553         reset : function()
48554         {
48555             this.setValue(this.defaultDialCode);
48556             this.validate();
48557         },
48558         
48559         hiddenEl : function()
48560         {
48561             return this.el.select('input.hidden-tel-input',true).first();
48562         },
48563         
48564         // after setting val
48565         onKeyUp : function(e){
48566             this.setValue(this.getValue());
48567         },
48568         
48569         onKeyPress : function(e){
48570             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48571                 e.stopEvent();
48572             }
48573         }
48574         
48575 });
48576 /**
48577  * @class Roo.bootstrap.form.MoneyField
48578  * @extends Roo.bootstrap.form.ComboBox
48579  * Bootstrap MoneyField class
48580  * 
48581  * @constructor
48582  * Create a new MoneyField.
48583  * @param {Object} config Configuration options
48584  */
48585
48586 Roo.bootstrap.form.MoneyField = function(config) {
48587     
48588     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48589     
48590 };
48591
48592 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48593     
48594     /**
48595      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48596      */
48597     allowDecimals : true,
48598     /**
48599      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48600      */
48601     decimalSeparator : ".",
48602     /**
48603      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48604      */
48605     decimalPrecision : 0,
48606     /**
48607      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48608      */
48609     allowNegative : true,
48610     /**
48611      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48612      */
48613     allowZero: true,
48614     /**
48615      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48616      */
48617     minValue : Number.NEGATIVE_INFINITY,
48618     /**
48619      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48620      */
48621     maxValue : Number.MAX_VALUE,
48622     /**
48623      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48624      */
48625     minText : "The minimum value for this field is {0}",
48626     /**
48627      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48628      */
48629     maxText : "The maximum value for this field is {0}",
48630     /**
48631      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48632      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48633      */
48634     nanText : "{0} is not a valid number",
48635     /**
48636      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48637      */
48638     castInt : true,
48639     /**
48640      * @cfg {String} defaults currency of the MoneyField
48641      * value should be in lkey
48642      */
48643     defaultCurrency : false,
48644     /**
48645      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48646      */
48647     thousandsDelimiter : false,
48648     /**
48649      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48650      */
48651     max_length: false,
48652     
48653     inputlg : 9,
48654     inputmd : 9,
48655     inputsm : 9,
48656     inputxs : 6,
48657      /**
48658      * @cfg {Roo.data.Store} store  Store to lookup currency??
48659      */
48660     store : false,
48661     
48662     getAutoCreate : function()
48663     {
48664         var align = this.labelAlign || this.parentLabelAlign();
48665         
48666         var id = Roo.id();
48667
48668         var cfg = {
48669             cls: 'form-group',
48670             cn: []
48671         };
48672
48673         var input =  {
48674             tag: 'input',
48675             id : id,
48676             cls : 'form-control roo-money-amount-input',
48677             autocomplete: 'new-password'
48678         };
48679         
48680         var hiddenInput = {
48681             tag: 'input',
48682             type: 'hidden',
48683             id: Roo.id(),
48684             cls: 'hidden-number-input'
48685         };
48686         
48687         if(this.max_length) {
48688             input.maxlength = this.max_length; 
48689         }
48690         
48691         if (this.name) {
48692             hiddenInput.name = this.name;
48693         }
48694
48695         if (this.disabled) {
48696             input.disabled = true;
48697         }
48698
48699         var clg = 12 - this.inputlg;
48700         var cmd = 12 - this.inputmd;
48701         var csm = 12 - this.inputsm;
48702         var cxs = 12 - this.inputxs;
48703         
48704         var container = {
48705             tag : 'div',
48706             cls : 'row roo-money-field',
48707             cn : [
48708                 {
48709                     tag : 'div',
48710                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48711                     cn : [
48712                         {
48713                             tag : 'div',
48714                             cls: 'roo-select2-container input-group',
48715                             cn: [
48716                                 {
48717                                     tag : 'input',
48718                                     cls : 'form-control roo-money-currency-input',
48719                                     autocomplete: 'new-password',
48720                                     readOnly : 1,
48721                                     name : this.currencyName
48722                                 },
48723                                 {
48724                                     tag :'span',
48725                                     cls : 'input-group-addon',
48726                                     cn : [
48727                                         {
48728                                             tag: 'span',
48729                                             cls: 'caret'
48730                                         }
48731                                     ]
48732                                 }
48733                             ]
48734                         }
48735                     ]
48736                 },
48737                 {
48738                     tag : 'div',
48739                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48740                     cn : [
48741                         {
48742                             tag: 'div',
48743                             cls: this.hasFeedback ? 'has-feedback' : '',
48744                             cn: [
48745                                 input
48746                             ]
48747                         }
48748                     ]
48749                 }
48750             ]
48751             
48752         };
48753         
48754         if (this.fieldLabel.length) {
48755             var indicator = {
48756                 tag: 'i',
48757                 tooltip: 'This field is required'
48758             };
48759
48760             var label = {
48761                 tag: 'label',
48762                 'for':  id,
48763                 cls: 'control-label',
48764                 cn: []
48765             };
48766
48767             var label_text = {
48768                 tag: 'span',
48769                 html: this.fieldLabel
48770             };
48771
48772             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48773             label.cn = [
48774                 indicator,
48775                 label_text
48776             ];
48777
48778             if(this.indicatorpos == 'right') {
48779                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48780                 label.cn = [
48781                     label_text,
48782                     indicator
48783                 ];
48784             }
48785
48786             if(align == 'left') {
48787                 container = {
48788                     tag: 'div',
48789                     cn: [
48790                         container
48791                     ]
48792                 };
48793
48794                 if(this.labelWidth > 12){
48795                     label.style = "width: " + this.labelWidth + 'px';
48796                 }
48797                 if(this.labelWidth < 13 && this.labelmd == 0){
48798                     this.labelmd = this.labelWidth;
48799                 }
48800                 if(this.labellg > 0){
48801                     label.cls += ' col-lg-' + this.labellg;
48802                     input.cls += ' col-lg-' + (12 - this.labellg);
48803                 }
48804                 if(this.labelmd > 0){
48805                     label.cls += ' col-md-' + this.labelmd;
48806                     container.cls += ' col-md-' + (12 - this.labelmd);
48807                 }
48808                 if(this.labelsm > 0){
48809                     label.cls += ' col-sm-' + this.labelsm;
48810                     container.cls += ' col-sm-' + (12 - this.labelsm);
48811                 }
48812                 if(this.labelxs > 0){
48813                     label.cls += ' col-xs-' + this.labelxs;
48814                     container.cls += ' col-xs-' + (12 - this.labelxs);
48815                 }
48816             }
48817         }
48818
48819         cfg.cn = [
48820             label,
48821             container,
48822             hiddenInput
48823         ];
48824         
48825         var settings = this;
48826
48827         ['xs','sm','md','lg'].map(function(size){
48828             if (settings[size]) {
48829                 cfg.cls += ' col-' + size + '-' + settings[size];
48830             }
48831         });
48832         
48833         return cfg;
48834     },
48835     
48836     initEvents : function()
48837     {
48838         this.indicator = this.indicatorEl();
48839         
48840         this.initCurrencyEvent();
48841         
48842         this.initNumberEvent();
48843     },
48844     
48845     initCurrencyEvent : function()
48846     {
48847         if (!this.store) {
48848             throw "can not find store for combo";
48849         }
48850         
48851         this.store = Roo.factory(this.store, Roo.data);
48852         this.store.parent = this;
48853         
48854         this.createList();
48855         
48856         this.triggerEl = this.el.select('.input-group-addon', true).first();
48857         
48858         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48859         
48860         var _this = this;
48861         
48862         (function(){
48863             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48864             _this.list.setWidth(lw);
48865         }).defer(100);
48866         
48867         this.list.on('mouseover', this.onViewOver, this);
48868         this.list.on('mousemove', this.onViewMove, this);
48869         this.list.on('scroll', this.onViewScroll, this);
48870         
48871         if(!this.tpl){
48872             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48873         }
48874         
48875         this.view = new Roo.View(this.list, this.tpl, {
48876             singleSelect:true, store: this.store, selectedClass: this.selectedClass
48877         });
48878         
48879         this.view.on('click', this.onViewClick, this);
48880         
48881         this.store.on('beforeload', this.onBeforeLoad, this);
48882         this.store.on('load', this.onLoad, this);
48883         this.store.on('loadexception', this.onLoadException, this);
48884         
48885         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48886             "up" : function(e){
48887                 this.inKeyMode = true;
48888                 this.selectPrev();
48889             },
48890
48891             "down" : function(e){
48892                 if(!this.isExpanded()){
48893                     this.onTriggerClick();
48894                 }else{
48895                     this.inKeyMode = true;
48896                     this.selectNext();
48897                 }
48898             },
48899
48900             "enter" : function(e){
48901                 this.collapse();
48902                 
48903                 if(this.fireEvent("specialkey", this, e)){
48904                     this.onViewClick(false);
48905                 }
48906                 
48907                 return true;
48908             },
48909
48910             "esc" : function(e){
48911                 this.collapse();
48912             },
48913
48914             "tab" : function(e){
48915                 this.collapse();
48916                 
48917                 if(this.fireEvent("specialkey", this, e)){
48918                     this.onViewClick(false);
48919                 }
48920                 
48921                 return true;
48922             },
48923
48924             scope : this,
48925
48926             doRelay : function(foo, bar, hname){
48927                 if(hname == 'down' || this.scope.isExpanded()){
48928                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48929                 }
48930                 return true;
48931             },
48932
48933             forceKeyDown: true
48934         });
48935         
48936         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48937         
48938     },
48939     
48940     initNumberEvent : function(e)
48941     {
48942         this.inputEl().on("keydown" , this.fireKey,  this);
48943         this.inputEl().on("focus", this.onFocus,  this);
48944         this.inputEl().on("blur", this.onBlur,  this);
48945         
48946         this.inputEl().relayEvent('keyup', this);
48947         
48948         if(this.indicator){
48949             this.indicator.addClass('invisible');
48950         }
48951  
48952         this.originalValue = this.getValue();
48953         
48954         if(this.validationEvent == 'keyup'){
48955             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48956             this.inputEl().on('keyup', this.filterValidation, this);
48957         }
48958         else if(this.validationEvent !== false){
48959             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48960         }
48961         
48962         if(this.selectOnFocus){
48963             this.on("focus", this.preFocus, this);
48964             
48965         }
48966         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48967             this.inputEl().on("keypress", this.filterKeys, this);
48968         } else {
48969             this.inputEl().relayEvent('keypress', this);
48970         }
48971         
48972         var allowed = "0123456789";
48973         
48974         if(this.allowDecimals){
48975             allowed += this.decimalSeparator;
48976         }
48977         
48978         if(this.allowNegative){
48979             allowed += "-";
48980         }
48981         
48982         if(this.thousandsDelimiter) {
48983             allowed += ",";
48984         }
48985         
48986         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48987         
48988         var keyPress = function(e){
48989             
48990             var k = e.getKey();
48991             
48992             var c = e.getCharCode();
48993             
48994             if(
48995                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48996                     allowed.indexOf(String.fromCharCode(c)) === -1
48997             ){
48998                 e.stopEvent();
48999                 return;
49000             }
49001             
49002             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
49003                 return;
49004             }
49005             
49006             if(allowed.indexOf(String.fromCharCode(c)) === -1){
49007                 e.stopEvent();
49008             }
49009         };
49010         
49011         this.inputEl().on("keypress", keyPress, this);
49012         
49013     },
49014     
49015     onTriggerClick : function(e)
49016     {   
49017         if(this.disabled){
49018             return;
49019         }
49020         
49021         this.page = 0;
49022         this.loadNext = false;
49023         
49024         if(this.isExpanded()){
49025             this.collapse();
49026             return;
49027         }
49028         
49029         this.hasFocus = true;
49030         
49031         if(this.triggerAction == 'all') {
49032             this.doQuery(this.allQuery, true);
49033             return;
49034         }
49035         
49036         this.doQuery(this.getRawValue());
49037     },
49038     
49039     getCurrency : function()
49040     {   
49041         var v = this.currencyEl().getValue();
49042         
49043         return v;
49044     },
49045     
49046     restrictHeight : function()
49047     {
49048         this.list.alignTo(this.currencyEl(), this.listAlign);
49049         this.list.alignTo(this.currencyEl(), this.listAlign);
49050     },
49051     
49052     onViewClick : function(view, doFocus, el, e)
49053     {
49054         var index = this.view.getSelectedIndexes()[0];
49055         
49056         var r = this.store.getAt(index);
49057         
49058         if(r){
49059             this.onSelect(r, index);
49060         }
49061     },
49062     
49063     onSelect : function(record, index){
49064         
49065         if(this.fireEvent('beforeselect', this, record, index) !== false){
49066         
49067             this.setFromCurrencyData(index > -1 ? record.data : false);
49068             
49069             this.collapse();
49070             
49071             this.fireEvent('select', this, record, index);
49072         }
49073     },
49074     
49075     setFromCurrencyData : function(o)
49076     {
49077         var currency = '';
49078         
49079         this.lastCurrency = o;
49080         
49081         if (this.currencyField) {
49082             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49083         } else {
49084             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
49085         }
49086         
49087         this.lastSelectionText = currency;
49088         
49089         //setting default currency
49090         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49091             this.setCurrency(this.defaultCurrency);
49092             return;
49093         }
49094         
49095         this.setCurrency(currency);
49096     },
49097     
49098     setFromData : function(o)
49099     {
49100         var c = {};
49101         
49102         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49103         
49104         this.setFromCurrencyData(c);
49105         
49106         var value = '';
49107         
49108         if (this.name) {
49109             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49110         } else {
49111             Roo.log('no value set for '+ (this.name ? this.name : this.id));
49112         }
49113         
49114         this.setValue(value);
49115         
49116     },
49117     
49118     setCurrency : function(v)
49119     {   
49120         this.currencyValue = v;
49121         
49122         if(this.rendered){
49123             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49124             this.validate();
49125         }
49126     },
49127     
49128     setValue : function(v)
49129     {
49130         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49131         
49132         this.value = v;
49133         
49134         if(this.rendered){
49135             
49136             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49137             
49138             this.inputEl().dom.value = (v == '') ? '' :
49139                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49140             
49141             if(!this.allowZero && v === '0') {
49142                 this.hiddenEl().dom.value = '';
49143                 this.inputEl().dom.value = '';
49144             }
49145             
49146             this.validate();
49147         }
49148     },
49149     
49150     getRawValue : function()
49151     {
49152         var v = this.inputEl().getValue();
49153         
49154         return v;
49155     },
49156     
49157     getValue : function()
49158     {
49159         return this.fixPrecision(this.parseValue(this.getRawValue()));
49160     },
49161     
49162     parseValue : function(value)
49163     {
49164         if(this.thousandsDelimiter) {
49165             value += "";
49166             r = new RegExp(",", "g");
49167             value = value.replace(r, "");
49168         }
49169         
49170         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49171         return isNaN(value) ? '' : value;
49172         
49173     },
49174     
49175     fixPrecision : function(value)
49176     {
49177         if(this.thousandsDelimiter) {
49178             value += "";
49179             r = new RegExp(",", "g");
49180             value = value.replace(r, "");
49181         }
49182         
49183         var nan = isNaN(value);
49184         
49185         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49186             return nan ? '' : value;
49187         }
49188         return parseFloat(value).toFixed(this.decimalPrecision);
49189     },
49190     
49191     decimalPrecisionFcn : function(v)
49192     {
49193         return Math.floor(v);
49194     },
49195     
49196     validateValue : function(value)
49197     {
49198         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49199             return false;
49200         }
49201         
49202         var num = this.parseValue(value);
49203         
49204         if(isNaN(num)){
49205             this.markInvalid(String.format(this.nanText, value));
49206             return false;
49207         }
49208         
49209         if(num < this.minValue){
49210             this.markInvalid(String.format(this.minText, this.minValue));
49211             return false;
49212         }
49213         
49214         if(num > this.maxValue){
49215             this.markInvalid(String.format(this.maxText, this.maxValue));
49216             return false;
49217         }
49218         
49219         return true;
49220     },
49221     
49222     validate : function()
49223     {
49224         if(this.disabled || this.allowBlank){
49225             this.markValid();
49226             return true;
49227         }
49228         
49229         var currency = this.getCurrency();
49230         
49231         if(this.validateValue(this.getRawValue()) && currency.length){
49232             this.markValid();
49233             return true;
49234         }
49235         
49236         this.markInvalid();
49237         return false;
49238     },
49239     
49240     getName: function()
49241     {
49242         return this.name;
49243     },
49244     
49245     beforeBlur : function()
49246     {
49247         if(!this.castInt){
49248             return;
49249         }
49250         
49251         var v = this.parseValue(this.getRawValue());
49252         
49253         if(v || v == 0){
49254             this.setValue(v);
49255         }
49256     },
49257     
49258     onBlur : function()
49259     {
49260         this.beforeBlur();
49261         
49262         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49263             //this.el.removeClass(this.focusClass);
49264         }
49265         
49266         this.hasFocus = false;
49267         
49268         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49269             this.validate();
49270         }
49271         
49272         var v = this.getValue();
49273         
49274         if(String(v) !== String(this.startValue)){
49275             this.fireEvent('change', this, v, this.startValue);
49276         }
49277         
49278         this.fireEvent("blur", this);
49279     },
49280     
49281     inputEl : function()
49282     {
49283         return this.el.select('.roo-money-amount-input', true).first();
49284     },
49285     
49286     currencyEl : function()
49287     {
49288         return this.el.select('.roo-money-currency-input', true).first();
49289     },
49290     
49291     hiddenEl : function()
49292     {
49293         return this.el.select('input.hidden-number-input',true).first();
49294     }
49295     
49296 });/**
49297  * @class Roo.bootstrap.BezierSignature
49298  * @extends Roo.bootstrap.Component
49299  * Bootstrap BezierSignature class
49300  * This script refer to:
49301  *    Title: Signature Pad
49302  *    Author: szimek
49303  *    Availability: https://github.com/szimek/signature_pad
49304  *
49305  * @constructor
49306  * Create a new BezierSignature
49307  * @param {Object} config The config object
49308  */
49309
49310 Roo.bootstrap.BezierSignature = function(config){
49311     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49312     this.addEvents({
49313         "resize" : true
49314     });
49315 };
49316
49317 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49318 {
49319      
49320     curve_data: [],
49321     
49322     is_empty: true,
49323     
49324     mouse_btn_down: true,
49325     
49326     /**
49327      * @cfg {int} canvas height
49328      */
49329     canvas_height: '200px',
49330     
49331     /**
49332      * @cfg {float|function} Radius of a single dot.
49333      */ 
49334     dot_size: false,
49335     
49336     /**
49337      * @cfg {float} Minimum width of a line. Defaults to 0.5.
49338      */
49339     min_width: 0.5,
49340     
49341     /**
49342      * @cfg {float} Maximum width of a line. Defaults to 2.5.
49343      */
49344     max_width: 2.5,
49345     
49346     /**
49347      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49348      */
49349     throttle: 16,
49350     
49351     /**
49352      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49353      */
49354     min_distance: 5,
49355     
49356     /**
49357      * @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.
49358      */
49359     bg_color: 'rgba(0, 0, 0, 0)',
49360     
49361     /**
49362      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49363      */
49364     dot_color: 'black',
49365     
49366     /**
49367      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49368      */ 
49369     velocity_filter_weight: 0.7,
49370     
49371     /**
49372      * @cfg {function} Callback when stroke begin. 
49373      */
49374     onBegin: false,
49375     
49376     /**
49377      * @cfg {function} Callback when stroke end.
49378      */
49379     onEnd: false,
49380     
49381     getAutoCreate : function()
49382     {
49383         var cls = 'roo-signature column';
49384         
49385         if(this.cls){
49386             cls += ' ' + this.cls;
49387         }
49388         
49389         var col_sizes = [
49390             'lg',
49391             'md',
49392             'sm',
49393             'xs'
49394         ];
49395         
49396         for(var i = 0; i < col_sizes.length; i++) {
49397             if(this[col_sizes[i]]) {
49398                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49399             }
49400         }
49401         
49402         var cfg = {
49403             tag: 'div',
49404             cls: cls,
49405             cn: [
49406                 {
49407                     tag: 'div',
49408                     cls: 'roo-signature-body',
49409                     cn: [
49410                         {
49411                             tag: 'canvas',
49412                             cls: 'roo-signature-body-canvas',
49413                             height: this.canvas_height,
49414                             width: this.canvas_width
49415                         }
49416                     ]
49417                 },
49418                 {
49419                     tag: 'input',
49420                     type: 'file',
49421                     style: 'display: none'
49422                 }
49423             ]
49424         };
49425         
49426         return cfg;
49427     },
49428     
49429     initEvents: function() 
49430     {
49431         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49432         
49433         var canvas = this.canvasEl();
49434         
49435         // mouse && touch event swapping...
49436         canvas.dom.style.touchAction = 'none';
49437         canvas.dom.style.msTouchAction = 'none';
49438         
49439         this.mouse_btn_down = false;
49440         canvas.on('mousedown', this._handleMouseDown, this);
49441         canvas.on('mousemove', this._handleMouseMove, this);
49442         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49443         
49444         if (window.PointerEvent) {
49445             canvas.on('pointerdown', this._handleMouseDown, this);
49446             canvas.on('pointermove', this._handleMouseMove, this);
49447             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49448         }
49449         
49450         if ('ontouchstart' in window) {
49451             canvas.on('touchstart', this._handleTouchStart, this);
49452             canvas.on('touchmove', this._handleTouchMove, this);
49453             canvas.on('touchend', this._handleTouchEnd, this);
49454         }
49455         
49456         Roo.EventManager.onWindowResize(this.resize, this, true);
49457         
49458         // file input event
49459         this.fileEl().on('change', this.uploadImage, this);
49460         
49461         this.clear();
49462         
49463         this.resize();
49464     },
49465     
49466     resize: function(){
49467         
49468         var canvas = this.canvasEl().dom;
49469         var ctx = this.canvasElCtx();
49470         var img_data = false;
49471         
49472         if(canvas.width > 0) {
49473             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49474         }
49475         // setting canvas width will clean img data
49476         canvas.width = 0;
49477         
49478         var style = window.getComputedStyle ? 
49479             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49480             
49481         var padding_left = parseInt(style.paddingLeft) || 0;
49482         var padding_right = parseInt(style.paddingRight) || 0;
49483         
49484         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49485         
49486         if(img_data) {
49487             ctx.putImageData(img_data, 0, 0);
49488         }
49489     },
49490     
49491     _handleMouseDown: function(e)
49492     {
49493         if (e.browserEvent.which === 1) {
49494             this.mouse_btn_down = true;
49495             this.strokeBegin(e);
49496         }
49497     },
49498     
49499     _handleMouseMove: function (e)
49500     {
49501         if (this.mouse_btn_down) {
49502             this.strokeMoveUpdate(e);
49503         }
49504     },
49505     
49506     _handleMouseUp: function (e)
49507     {
49508         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49509             this.mouse_btn_down = false;
49510             this.strokeEnd(e);
49511         }
49512     },
49513     
49514     _handleTouchStart: function (e) {
49515         
49516         e.preventDefault();
49517         if (e.browserEvent.targetTouches.length === 1) {
49518             // var touch = e.browserEvent.changedTouches[0];
49519             // this.strokeBegin(touch);
49520             
49521              this.strokeBegin(e); // assume e catching the correct xy...
49522         }
49523     },
49524     
49525     _handleTouchMove: function (e) {
49526         e.preventDefault();
49527         // var touch = event.targetTouches[0];
49528         // _this._strokeMoveUpdate(touch);
49529         this.strokeMoveUpdate(e);
49530     },
49531     
49532     _handleTouchEnd: function (e) {
49533         var wasCanvasTouched = e.target === this.canvasEl().dom;
49534         if (wasCanvasTouched) {
49535             e.preventDefault();
49536             // var touch = event.changedTouches[0];
49537             // _this._strokeEnd(touch);
49538             this.strokeEnd(e);
49539         }
49540     },
49541     
49542     reset: function () {
49543         this._lastPoints = [];
49544         this._lastVelocity = 0;
49545         this._lastWidth = (this.min_width + this.max_width) / 2;
49546         this.canvasElCtx().fillStyle = this.dot_color;
49547     },
49548     
49549     strokeMoveUpdate: function(e)
49550     {
49551         this.strokeUpdate(e);
49552         
49553         if (this.throttle) {
49554             this.throttleStroke(this.strokeUpdate, this.throttle);
49555         }
49556         else {
49557             this.strokeUpdate(e);
49558         }
49559     },
49560     
49561     strokeBegin: function(e)
49562     {
49563         var newPointGroup = {
49564             color: this.dot_color,
49565             points: []
49566         };
49567         
49568         if (typeof this.onBegin === 'function') {
49569             this.onBegin(e);
49570         }
49571         
49572         this.curve_data.push(newPointGroup);
49573         this.reset();
49574         this.strokeUpdate(e);
49575     },
49576     
49577     strokeUpdate: function(e)
49578     {
49579         var rect = this.canvasEl().dom.getBoundingClientRect();
49580         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49581         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49582         var lastPoints = lastPointGroup.points;
49583         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49584         var isLastPointTooClose = lastPoint
49585             ? point.distanceTo(lastPoint) <= this.min_distance
49586             : false;
49587         var color = lastPointGroup.color;
49588         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49589             var curve = this.addPoint(point);
49590             if (!lastPoint) {
49591                 this.drawDot({color: color, point: point});
49592             }
49593             else if (curve) {
49594                 this.drawCurve({color: color, curve: curve});
49595             }
49596             lastPoints.push({
49597                 time: point.time,
49598                 x: point.x,
49599                 y: point.y
49600             });
49601         }
49602     },
49603     
49604     strokeEnd: function(e)
49605     {
49606         this.strokeUpdate(e);
49607         if (typeof this.onEnd === 'function') {
49608             this.onEnd(e);
49609         }
49610     },
49611     
49612     addPoint:  function (point) {
49613         var _lastPoints = this._lastPoints;
49614         _lastPoints.push(point);
49615         if (_lastPoints.length > 2) {
49616             if (_lastPoints.length === 3) {
49617                 _lastPoints.unshift(_lastPoints[0]);
49618             }
49619             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49620             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49621             _lastPoints.shift();
49622             return curve;
49623         }
49624         return null;
49625     },
49626     
49627     calculateCurveWidths: function (startPoint, endPoint) {
49628         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49629             (1 - this.velocity_filter_weight) * this._lastVelocity;
49630
49631         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49632         var widths = {
49633             end: newWidth,
49634             start: this._lastWidth
49635         };
49636         
49637         this._lastVelocity = velocity;
49638         this._lastWidth = newWidth;
49639         return widths;
49640     },
49641     
49642     drawDot: function (_a) {
49643         var color = _a.color, point = _a.point;
49644         var ctx = this.canvasElCtx();
49645         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49646         ctx.beginPath();
49647         this.drawCurveSegment(point.x, point.y, width);
49648         ctx.closePath();
49649         ctx.fillStyle = color;
49650         ctx.fill();
49651     },
49652     
49653     drawCurve: function (_a) {
49654         var color = _a.color, curve = _a.curve;
49655         var ctx = this.canvasElCtx();
49656         var widthDelta = curve.endWidth - curve.startWidth;
49657         var drawSteps = Math.floor(curve.length()) * 2;
49658         ctx.beginPath();
49659         ctx.fillStyle = color;
49660         for (var i = 0; i < drawSteps; i += 1) {
49661         var t = i / drawSteps;
49662         var tt = t * t;
49663         var ttt = tt * t;
49664         var u = 1 - t;
49665         var uu = u * u;
49666         var uuu = uu * u;
49667         var x = uuu * curve.startPoint.x;
49668         x += 3 * uu * t * curve.control1.x;
49669         x += 3 * u * tt * curve.control2.x;
49670         x += ttt * curve.endPoint.x;
49671         var y = uuu * curve.startPoint.y;
49672         y += 3 * uu * t * curve.control1.y;
49673         y += 3 * u * tt * curve.control2.y;
49674         y += ttt * curve.endPoint.y;
49675         var width = curve.startWidth + ttt * widthDelta;
49676         this.drawCurveSegment(x, y, width);
49677         }
49678         ctx.closePath();
49679         ctx.fill();
49680     },
49681     
49682     drawCurveSegment: function (x, y, width) {
49683         var ctx = this.canvasElCtx();
49684         ctx.moveTo(x, y);
49685         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49686         this.is_empty = false;
49687     },
49688     
49689     clear: function()
49690     {
49691         var ctx = this.canvasElCtx();
49692         var canvas = this.canvasEl().dom;
49693         ctx.fillStyle = this.bg_color;
49694         ctx.clearRect(0, 0, canvas.width, canvas.height);
49695         ctx.fillRect(0, 0, canvas.width, canvas.height);
49696         this.curve_data = [];
49697         this.reset();
49698         this.is_empty = true;
49699     },
49700     
49701     fileEl: function()
49702     {
49703         return  this.el.select('input',true).first();
49704     },
49705     
49706     canvasEl: function()
49707     {
49708         return this.el.select('canvas',true).first();
49709     },
49710     
49711     canvasElCtx: function()
49712     {
49713         return this.el.select('canvas',true).first().dom.getContext('2d');
49714     },
49715     
49716     getImage: function(type)
49717     {
49718         if(this.is_empty) {
49719             return false;
49720         }
49721         
49722         // encryption ?
49723         return this.canvasEl().dom.toDataURL('image/'+type, 1);
49724     },
49725     
49726     drawFromImage: function(img_src)
49727     {
49728         var img = new Image();
49729         
49730         img.onload = function(){
49731             this.canvasElCtx().drawImage(img, 0, 0);
49732         }.bind(this);
49733         
49734         img.src = img_src;
49735         
49736         this.is_empty = false;
49737     },
49738     
49739     selectImage: function()
49740     {
49741         this.fileEl().dom.click();
49742     },
49743     
49744     uploadImage: function(e)
49745     {
49746         var reader = new FileReader();
49747         
49748         reader.onload = function(e){
49749             var img = new Image();
49750             img.onload = function(){
49751                 this.reset();
49752                 this.canvasElCtx().drawImage(img, 0, 0);
49753             }.bind(this);
49754             img.src = e.target.result;
49755         }.bind(this);
49756         
49757         reader.readAsDataURL(e.target.files[0]);
49758     },
49759     
49760     // Bezier Point Constructor
49761     Point: (function () {
49762         function Point(x, y, time) {
49763             this.x = x;
49764             this.y = y;
49765             this.time = time || Date.now();
49766         }
49767         Point.prototype.distanceTo = function (start) {
49768             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49769         };
49770         Point.prototype.equals = function (other) {
49771             return this.x === other.x && this.y === other.y && this.time === other.time;
49772         };
49773         Point.prototype.velocityFrom = function (start) {
49774             return this.time !== start.time
49775             ? this.distanceTo(start) / (this.time - start.time)
49776             : 0;
49777         };
49778         return Point;
49779     }()),
49780     
49781     
49782     // Bezier Constructor
49783     Bezier: (function () {
49784         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49785             this.startPoint = startPoint;
49786             this.control2 = control2;
49787             this.control1 = control1;
49788             this.endPoint = endPoint;
49789             this.startWidth = startWidth;
49790             this.endWidth = endWidth;
49791         }
49792         Bezier.fromPoints = function (points, widths, scope) {
49793             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49794             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49795             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49796         };
49797         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49798             var dx1 = s1.x - s2.x;
49799             var dy1 = s1.y - s2.y;
49800             var dx2 = s2.x - s3.x;
49801             var dy2 = s2.y - s3.y;
49802             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49803             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49804             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49805             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49806             var dxm = m1.x - m2.x;
49807             var dym = m1.y - m2.y;
49808             var k = l2 / (l1 + l2);
49809             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49810             var tx = s2.x - cm.x;
49811             var ty = s2.y - cm.y;
49812             return {
49813                 c1: new scope.Point(m1.x + tx, m1.y + ty),
49814                 c2: new scope.Point(m2.x + tx, m2.y + ty)
49815             };
49816         };
49817         Bezier.prototype.length = function () {
49818             var steps = 10;
49819             var length = 0;
49820             var px;
49821             var py;
49822             for (var i = 0; i <= steps; i += 1) {
49823                 var t = i / steps;
49824                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49825                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49826                 if (i > 0) {
49827                     var xdiff = cx - px;
49828                     var ydiff = cy - py;
49829                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49830                 }
49831                 px = cx;
49832                 py = cy;
49833             }
49834             return length;
49835         };
49836         Bezier.prototype.point = function (t, start, c1, c2, end) {
49837             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49838             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49839             + (3.0 * c2 * (1.0 - t) * t * t)
49840             + (end * t * t * t);
49841         };
49842         return Bezier;
49843     }()),
49844     
49845     throttleStroke: function(fn, wait) {
49846       if (wait === void 0) { wait = 250; }
49847       var previous = 0;
49848       var timeout = null;
49849       var result;
49850       var storedContext;
49851       var storedArgs;
49852       var later = function () {
49853           previous = Date.now();
49854           timeout = null;
49855           result = fn.apply(storedContext, storedArgs);
49856           if (!timeout) {
49857               storedContext = null;
49858               storedArgs = [];
49859           }
49860       };
49861       return function wrapper() {
49862           var args = [];
49863           for (var _i = 0; _i < arguments.length; _i++) {
49864               args[_i] = arguments[_i];
49865           }
49866           var now = Date.now();
49867           var remaining = wait - (now - previous);
49868           storedContext = this;
49869           storedArgs = args;
49870           if (remaining <= 0 || remaining > wait) {
49871               if (timeout) {
49872                   clearTimeout(timeout);
49873                   timeout = null;
49874               }
49875               previous = now;
49876               result = fn.apply(storedContext, storedArgs);
49877               if (!timeout) {
49878                   storedContext = null;
49879                   storedArgs = [];
49880               }
49881           }
49882           else if (!timeout) {
49883               timeout = window.setTimeout(later, remaining);
49884           }
49885           return result;
49886       };
49887   }
49888   
49889 });
49890
49891  
49892
49893  // old names for form elements
49894 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
49895 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
49896 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
49897 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
49898 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
49899 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
49900 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
49901 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
49902 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
49903 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
49904 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
49905 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
49906 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
49907 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
49908 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
49909 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
49910 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
49911 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
49912 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
49913 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
49914 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
49915 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
49916 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
49917 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
49918 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
49919 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
49920
49921 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
49922 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49923
49924 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
49925 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
49926
49927 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
49928 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49929 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
49930 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
49931