support resize on bootstrap html editor
[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 is always the multiple of 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} css styling for resizing. (used on bootstrap only)
30844      */
30845     resize : false,
30846      /**
30847      * @cfg {Number} height (in pixels)
30848      */   
30849     height: 300,
30850    /**
30851      * @cfg {Number} width (in pixels)
30852      */   
30853     width: 500,
30854      /**
30855      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30856      *         if you are doing an email editor, this probably needs disabling, it's designed
30857      */
30858     autoClean: true,
30859     
30860     /**
30861      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30862      */
30863     enableBlocks : true,
30864     /**
30865      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30866      * 
30867      */
30868     stylesheets: false,
30869      /**
30870      * @cfg {String} language default en - language of text (usefull for rtl languages)
30871      * 
30872      */
30873     language: 'en',
30874     
30875     /**
30876      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30877      *          - by default they are stripped - if you are editing email you may need this.
30878      */
30879     allowComments: false,
30880     // id of frame..
30881     frameId: false,
30882     
30883     // private properties
30884     validationEvent : false,
30885     deferHeight: true,
30886     initialized : false,
30887     activated : false,
30888     sourceEditMode : false,
30889     onFocus : Roo.emptyFn,
30890     iframePad:3,
30891     hideMode:'offsets',
30892     
30893     clearUp: true,
30894     
30895     // blacklist + whitelisted elements..
30896     black: false,
30897     white: false,
30898      
30899     bodyCls : '',
30900
30901     
30902     undoManager : false,
30903     /**
30904      * Protected method that will not generally be called directly. It
30905      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30906      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30907      */
30908     getDocMarkup : function(){
30909         // body styles..
30910         var st = '';
30911         
30912         // inherit styels from page...?? 
30913         if (this.stylesheets === false) {
30914             
30915             Roo.get(document.head).select('style').each(function(node) {
30916                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30917             });
30918             
30919             Roo.get(document.head).select('link').each(function(node) { 
30920                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30921             });
30922             
30923         } else if (!this.stylesheets.length) {
30924                 // simple..
30925                 st = '<style type="text/css">' +
30926                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30927                    '</style>';
30928         } else {
30929             for (var i in this.stylesheets) {
30930                 if (typeof(this.stylesheets[i]) != 'string') {
30931                     continue;
30932                 }
30933                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30934             }
30935             
30936         }
30937         
30938         st +=  '<style type="text/css">' +
30939             'IMG { cursor: pointer } ' +
30940         '</style>';
30941         
30942         st += '<meta name="google" content="notranslate">';
30943         
30944         var cls = 'notranslate roo-htmleditor-body';
30945         
30946         if(this.bodyCls.length){
30947             cls += ' ' + this.bodyCls;
30948         }
30949         
30950         return '<html  class="notranslate" translate="no"><head>' + st  +
30951             //<style type="text/css">' +
30952             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30953             //'</style>' +
30954             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30955     },
30956
30957     // private
30958     onRender : function(ct, position)
30959     {
30960         var _t = this;
30961         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30962         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30963         
30964         
30965         this.el.dom.style.border = '0 none';
30966         this.el.dom.setAttribute('tabIndex', -1);
30967         this.el.addClass('x-hidden hide');
30968         
30969         
30970         
30971         if(Roo.isIE){ // fix IE 1px bogus margin
30972             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30973         }
30974        
30975         
30976         this.frameId = Roo.id();
30977         
30978         var ifcfg = {
30979             tag: 'iframe',
30980             cls: 'form-control', // bootstrap..
30981             id: this.frameId,
30982             name: this.frameId,
30983             frameBorder : 'no',
30984             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
30985         };
30986         if (this.resize) {
30987             ifcfg.style = { resize : this.resize };
30988         }
30989         
30990         var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
30991         
30992         
30993         this.iframe = iframe.dom;
30994
30995         this.assignDocWin();
30996         
30997         this.doc.designMode = 'on';
30998        
30999         this.doc.open();
31000         this.doc.write(this.getDocMarkup());
31001         this.doc.close();
31002
31003         
31004         var task = { // must defer to wait for browser to be ready
31005             run : function(){
31006                 //console.log("run task?" + this.doc.readyState);
31007                 this.assignDocWin();
31008                 if(this.doc.body || this.doc.readyState == 'complete'){
31009                     try {
31010                         this.doc.designMode="on";
31011                         
31012                     } catch (e) {
31013                         return;
31014                     }
31015                     Roo.TaskMgr.stop(task);
31016                     this.initEditor.defer(10, this);
31017                 }
31018             },
31019             interval : 10,
31020             duration: 10000,
31021             scope: this
31022         };
31023         Roo.TaskMgr.start(task);
31024
31025     },
31026
31027     // private
31028     onResize : function(w, h)
31029     {
31030          Roo.log('resize: ' +w + ',' + h );
31031         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31032         if(!this.iframe){
31033             return;
31034         }
31035         if(typeof w == 'number'){
31036             
31037             this.iframe.style.width = w + 'px';
31038         }
31039         if(typeof h == 'number'){
31040             
31041             this.iframe.style.height = h + 'px';
31042             if(this.doc){
31043                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31044             }
31045         }
31046         
31047     },
31048
31049     /**
31050      * Toggles the editor between standard and source edit mode.
31051      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31052      */
31053     toggleSourceEdit : function(sourceEditMode){
31054         
31055         this.sourceEditMode = sourceEditMode === true;
31056         
31057         if(this.sourceEditMode){
31058  
31059             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
31060             
31061         }else{
31062             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31063             //this.iframe.className = '';
31064             this.deferFocus();
31065         }
31066         //this.setSize(this.owner.wrap.getSize());
31067         //this.fireEvent('editmodechange', this, this.sourceEditMode);
31068     },
31069
31070     
31071   
31072
31073     /**
31074      * Protected method that will not generally be called directly. If you need/want
31075      * custom HTML cleanup, this is the method you should override.
31076      * @param {String} html The HTML to be cleaned
31077      * return {String} The cleaned HTML
31078      */
31079     cleanHtml : function(html)
31080     {
31081         html = String(html);
31082         if(html.length > 5){
31083             if(Roo.isSafari){ // strip safari nonsense
31084                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31085             }
31086         }
31087         if(html == '&nbsp;'){
31088             html = '';
31089         }
31090         return html;
31091     },
31092
31093     /**
31094      * HTML Editor -> Textarea
31095      * Protected method that will not generally be called directly. Syncs the contents
31096      * of the editor iframe with the textarea.
31097      */
31098     syncValue : function()
31099     {
31100         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31101         if(this.initialized){
31102             
31103             if (this.undoManager) {
31104                 this.undoManager.addEvent();
31105             }
31106
31107             
31108             var bd = (this.doc.body || this.doc.documentElement);
31109            
31110             
31111             var sel = this.win.getSelection();
31112             
31113             var div = document.createElement('div');
31114             div.innerHTML = bd.innerHTML;
31115             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31116             if (gtx.length > 0) {
31117                 var rm = gtx.item(0).parentNode;
31118                 rm.parentNode.removeChild(rm);
31119             }
31120             
31121            
31122             if (this.enableBlocks) {
31123                 new Roo.htmleditor.FilterBlock({ node : div });
31124             }
31125             
31126             var html = div.innerHTML;
31127             
31128             //?? tidy?
31129             if (this.autoClean) {
31130                 
31131                 new Roo.htmleditor.FilterAttributes({
31132                     node : div,
31133                     attrib_white : [
31134                             'href',
31135                             'src',
31136                             'name',
31137                             'align',
31138                             'colspan',
31139                             'rowspan',
31140                             'data-display',
31141                             'data-width',
31142                             'start' ,
31143                             'style',
31144                             // youtube embed.
31145                             'class',
31146                             'allowfullscreen',
31147                             'frameborder',
31148                             'width',
31149                             'height',
31150                             'alt'
31151                             ],
31152                     attrib_clean : ['href', 'src' ] 
31153                 });
31154                 
31155                 var tidy = new Roo.htmleditor.TidySerializer({
31156                     inner:  true
31157                 });
31158                 html  = tidy.serialize(div);
31159                 
31160             }
31161             
31162             
31163             if(Roo.isSafari){
31164                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31165                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31166                 if(m && m[1]){
31167                     html = '<div style="'+m[0]+'">' + html + '</div>';
31168                 }
31169             }
31170             html = this.cleanHtml(html);
31171             // fix up the special chars.. normaly like back quotes in word...
31172             // however we do not want to do this with chinese..
31173             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31174                 
31175                 var cc = match.charCodeAt();
31176
31177                 // Get the character value, handling surrogate pairs
31178                 if (match.length == 2) {
31179                     // It's a surrogate pair, calculate the Unicode code point
31180                     var high = match.charCodeAt(0) - 0xD800;
31181                     var low  = match.charCodeAt(1) - 0xDC00;
31182                     cc = (high * 0x400) + low + 0x10000;
31183                 }  else if (
31184                     (cc >= 0x4E00 && cc < 0xA000 ) ||
31185                     (cc >= 0x3400 && cc < 0x4E00 ) ||
31186                     (cc >= 0xf900 && cc < 0xfb00 )
31187                 ) {
31188                         return match;
31189                 }  
31190          
31191                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31192                 return "&#" + cc + ";";
31193                 
31194                 
31195             });
31196             
31197             
31198              
31199             if(this.owner.fireEvent('beforesync', this, html) !== false){
31200                 this.el.dom.value = html;
31201                 this.owner.fireEvent('sync', this, html);
31202             }
31203         }
31204     },
31205
31206     /**
31207      * TEXTAREA -> EDITABLE
31208      * Protected method that will not generally be called directly. Pushes the value of the textarea
31209      * into the iframe editor.
31210      */
31211     pushValue : function()
31212     {
31213         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31214         if(this.initialized){
31215             var v = this.el.dom.value.trim();
31216             
31217             
31218             if(this.owner.fireEvent('beforepush', this, v) !== false){
31219                 var d = (this.doc.body || this.doc.documentElement);
31220                 d.innerHTML = v;
31221                  
31222                 this.el.dom.value = d.innerHTML;
31223                 this.owner.fireEvent('push', this, v);
31224             }
31225             if (this.autoClean) {
31226                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31227                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31228             }
31229             if (this.enableBlocks) {
31230                 Roo.htmleditor.Block.initAll(this.doc.body);
31231             }
31232             
31233             this.updateLanguage();
31234             
31235             var lc = this.doc.body.lastChild;
31236             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31237                 // add an extra line at the end.
31238                 this.doc.body.appendChild(this.doc.createElement('br'));
31239             }
31240             
31241             
31242         }
31243     },
31244
31245     // private
31246     deferFocus : function(){
31247         this.focus.defer(10, this);
31248     },
31249
31250     // doc'ed in Field
31251     focus : function(){
31252         if(this.win && !this.sourceEditMode){
31253             this.win.focus();
31254         }else{
31255             this.el.focus();
31256         }
31257     },
31258     
31259     assignDocWin: function()
31260     {
31261         var iframe = this.iframe;
31262         
31263          if(Roo.isIE){
31264             this.doc = iframe.contentWindow.document;
31265             this.win = iframe.contentWindow;
31266         } else {
31267 //            if (!Roo.get(this.frameId)) {
31268 //                return;
31269 //            }
31270 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31271 //            this.win = Roo.get(this.frameId).dom.contentWindow;
31272             
31273             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31274                 return;
31275             }
31276             
31277             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31278             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31279         }
31280     },
31281     
31282     // private
31283     initEditor : function(){
31284         //console.log("INIT EDITOR");
31285         this.assignDocWin();
31286         
31287         
31288         
31289         this.doc.designMode="on";
31290         this.doc.open();
31291         this.doc.write(this.getDocMarkup());
31292         this.doc.close();
31293         
31294         var dbody = (this.doc.body || this.doc.documentElement);
31295         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31296         // this copies styles from the containing element into thsi one..
31297         // not sure why we need all of this..
31298         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31299         
31300         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31301         //ss['background-attachment'] = 'fixed'; // w3c
31302         dbody.bgProperties = 'fixed'; // ie
31303         dbody.setAttribute("translate", "no");
31304         
31305         //Roo.DomHelper.applyStyles(dbody, ss);
31306         Roo.EventManager.on(this.doc, {
31307              
31308             'mouseup': this.onEditorEvent,
31309             'dblclick': this.onEditorEvent,
31310             'click': this.onEditorEvent,
31311             'keyup': this.onEditorEvent,
31312             
31313             buffer:100,
31314             scope: this
31315         });
31316         Roo.EventManager.on(this.doc, {
31317             'paste': this.onPasteEvent,
31318             scope : this
31319         });
31320         if(Roo.isGecko){
31321             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31322         }
31323         //??? needed???
31324         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31325             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31326         }
31327         this.initialized = true;
31328
31329         
31330         // initialize special key events - enter
31331         new Roo.htmleditor.KeyEnter({core : this});
31332         
31333          
31334         
31335         this.owner.fireEvent('initialize', this);
31336         this.pushValue();
31337     },
31338     // this is to prevent a href clicks resulting in a redirect?
31339    
31340     onPasteEvent : function(e,v)
31341     {
31342         // I think we better assume paste is going to be a dirty load of rubish from word..
31343         
31344         // even pasting into a 'email version' of this widget will have to clean up that mess.
31345         var cd = (e.browserEvent.clipboardData || window.clipboardData);
31346         
31347         // check what type of paste - if it's an image, then handle it differently.
31348         if (cd.files && cd.files.length > 0) {
31349             // pasting images?
31350             var urlAPI = (window.createObjectURL && window) || 
31351                 (window.URL && URL.revokeObjectURL && URL) || 
31352                 (window.webkitURL && webkitURL);
31353     
31354             var url = urlAPI.createObjectURL( cd.files[0]);
31355             this.insertAtCursor('<img src=" + url + ">');
31356             return false;
31357         }
31358         if (cd.types.indexOf('text/html') < 0 ) {
31359             return false;
31360         }
31361         var images = [];
31362         var html = cd.getData('text/html'); // clipboard event
31363         if (cd.types.indexOf('text/rtf') > -1) {
31364             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31365             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31366         }
31367         //Roo.log(images);
31368         //Roo.log(imgs);
31369         // fixme..
31370         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31371                        .map(function(g) { return g.toDataURL(); })
31372                        .filter(function(g) { return g != 'about:blank'; });
31373         
31374         //Roo.log(html);
31375         html = this.cleanWordChars(html);
31376         
31377         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31378         
31379         
31380         var sn = this.getParentElement();
31381         // check if d contains a table, and prevent nesting??
31382         //Roo.log(d.getElementsByTagName('table'));
31383         //Roo.log(sn);
31384         //Roo.log(sn.closest('table'));
31385         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31386             e.preventDefault();
31387             this.insertAtCursor("You can not nest tables");
31388             //Roo.log("prevent?"); // fixme - 
31389             return false;
31390         }
31391         
31392         
31393         
31394         if (images.length > 0) {
31395             // replace all v:imagedata - with img.
31396             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31397             Roo.each(ar, function(node) {
31398                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31399                 node.parentNode.removeChild(node);
31400             });
31401             
31402             
31403             Roo.each(d.getElementsByTagName('img'), function(img, i) {
31404                 img.setAttribute('src', images[i]);
31405             });
31406         }
31407         if (this.autoClean) {
31408             new Roo.htmleditor.FilterWord({ node : d });
31409             
31410             new Roo.htmleditor.FilterStyleToTag({ node : d });
31411             new Roo.htmleditor.FilterAttributes({
31412                 node : d,
31413                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31414                 attrib_clean : ['href', 'src' ] 
31415             });
31416             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31417             // should be fonts..
31418             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31419             new Roo.htmleditor.FilterParagraph({ node : d });
31420             new Roo.htmleditor.FilterSpan({ node : d });
31421             new Roo.htmleditor.FilterLongBr({ node : d });
31422             new Roo.htmleditor.FilterComment({ node : d });
31423             
31424             
31425         }
31426         if (this.enableBlocks) {
31427                 
31428             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31429                 if (img.closest('figure')) { // assume!! that it's aready
31430                     return;
31431                 }
31432                 var fig  = new Roo.htmleditor.BlockFigure({
31433                     image_src  : img.src
31434                 });
31435                 fig.updateElement(img); // replace it..
31436                 
31437             });
31438         }
31439         
31440         
31441         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31442         if (this.enableBlocks) {
31443             Roo.htmleditor.Block.initAll(this.doc.body);
31444         }
31445          
31446         
31447         e.preventDefault();
31448         this.owner.fireEvent('paste', this);
31449         return false;
31450         // default behaveiour should be our local cleanup paste? (optional?)
31451         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31452         //this.owner.fireEvent('paste', e, v);
31453     },
31454     // private
31455     onDestroy : function(){
31456         
31457         
31458         
31459         if(this.rendered){
31460             
31461             //for (var i =0; i < this.toolbars.length;i++) {
31462             //    // fixme - ask toolbars for heights?
31463             //    this.toolbars[i].onDestroy();
31464            // }
31465             
31466             //this.wrap.dom.innerHTML = '';
31467             //this.wrap.remove();
31468         }
31469     },
31470
31471     // private
31472     onFirstFocus : function(){
31473         
31474         this.assignDocWin();
31475         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31476         
31477         this.activated = true;
31478          
31479     
31480         if(Roo.isGecko){ // prevent silly gecko errors
31481             this.win.focus();
31482             var s = this.win.getSelection();
31483             if(!s.focusNode || s.focusNode.nodeType != 3){
31484                 var r = s.getRangeAt(0);
31485                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31486                 r.collapse(true);
31487                 this.deferFocus();
31488             }
31489             try{
31490                 this.execCmd('useCSS', true);
31491                 this.execCmd('styleWithCSS', false);
31492             }catch(e){}
31493         }
31494         this.owner.fireEvent('activate', this);
31495     },
31496
31497     // private
31498     adjustFont: function(btn){
31499         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31500         //if(Roo.isSafari){ // safari
31501         //    adjust *= 2;
31502        // }
31503         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31504         if(Roo.isSafari){ // safari
31505             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31506             v =  (v < 10) ? 10 : v;
31507             v =  (v > 48) ? 48 : v;
31508             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31509             
31510         }
31511         
31512         
31513         v = Math.max(1, v+adjust);
31514         
31515         this.execCmd('FontSize', v  );
31516     },
31517
31518     onEditorEvent : function(e)
31519     {
31520          
31521         
31522         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31523             return; // we do not handle this.. (undo manager does..)
31524         }
31525         // in theory this detects if the last element is not a br, then we try and do that.
31526         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31527         if (e &&
31528             e.target.nodeName == 'BODY' &&
31529             e.type == "mouseup" &&
31530             this.doc.body.lastChild
31531            ) {
31532             var lc = this.doc.body.lastChild;
31533             // gtx-trans is google translate plugin adding crap.
31534             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31535                 lc = lc.previousSibling;
31536             }
31537             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31538             // if last element is <BR> - then dont do anything.
31539             
31540                 var ns = this.doc.createElement('br');
31541                 this.doc.body.appendChild(ns);
31542                 range = this.doc.createRange();
31543                 range.setStartAfter(ns);
31544                 range.collapse(true);
31545                 var sel = this.win.getSelection();
31546                 sel.removeAllRanges();
31547                 sel.addRange(range);
31548             }
31549         }
31550         
31551         
31552         
31553         this.fireEditorEvent(e);
31554       //  this.updateToolbar();
31555         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31556     },
31557     
31558     fireEditorEvent: function(e)
31559     {
31560         this.owner.fireEvent('editorevent', this, e);
31561     },
31562
31563     insertTag : function(tg)
31564     {
31565         // could be a bit smarter... -> wrap the current selected tRoo..
31566         if (tg.toLowerCase() == 'span' ||
31567             tg.toLowerCase() == 'code' ||
31568             tg.toLowerCase() == 'sup' ||
31569             tg.toLowerCase() == 'sub' 
31570             ) {
31571             
31572             range = this.createRange(this.getSelection());
31573             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31574             wrappingNode.appendChild(range.extractContents());
31575             range.insertNode(wrappingNode);
31576
31577             return;
31578             
31579             
31580             
31581         }
31582         this.execCmd("formatblock",   tg);
31583         this.undoManager.addEvent(); 
31584     },
31585     
31586     insertText : function(txt)
31587     {
31588         
31589         
31590         var range = this.createRange();
31591         range.deleteContents();
31592                //alert(Sender.getAttribute('label'));
31593                
31594         range.insertNode(this.doc.createTextNode(txt));
31595         this.undoManager.addEvent();
31596     } ,
31597     
31598      
31599
31600     /**
31601      * Executes a Midas editor command on the editor document and performs necessary focus and
31602      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31603      * @param {String} cmd The Midas command
31604      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31605      */
31606     relayCmd : function(cmd, value)
31607     {
31608         
31609         switch (cmd) {
31610             case 'justifyleft':
31611             case 'justifyright':
31612             case 'justifycenter':
31613                 // if we are in a cell, then we will adjust the
31614                 var n = this.getParentElement();
31615                 var td = n.closest('td');
31616                 if (td) {
31617                     var bl = Roo.htmleditor.Block.factory(td);
31618                     bl.textAlign = cmd.replace('justify','');
31619                     bl.updateElement();
31620                     this.owner.fireEvent('editorevent', this);
31621                     return;
31622                 }
31623                 this.execCmd('styleWithCSS', true); // 
31624                 break;
31625             case 'bold':
31626             case 'italic':
31627                 // if there is no selection, then we insert, and set the curson inside it..
31628                 this.execCmd('styleWithCSS', false); 
31629                 break;
31630                 
31631         
31632             default:
31633                 break;
31634         }
31635         
31636         
31637         this.win.focus();
31638         this.execCmd(cmd, value);
31639         this.owner.fireEvent('editorevent', this);
31640         //this.updateToolbar();
31641         this.owner.deferFocus();
31642     },
31643
31644     /**
31645      * Executes a Midas editor command directly on the editor document.
31646      * For visual commands, you should use {@link #relayCmd} instead.
31647      * <b>This should only be called after the editor is initialized.</b>
31648      * @param {String} cmd The Midas command
31649      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31650      */
31651     execCmd : function(cmd, value){
31652         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31653         this.syncValue();
31654     },
31655  
31656  
31657    
31658     /**
31659      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31660      * to insert tRoo.
31661      * @param {String} text | dom node.. 
31662      */
31663     insertAtCursor : function(text)
31664     {
31665         
31666         if(!this.activated){
31667             return;
31668         }
31669          
31670         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31671             this.win.focus();
31672             
31673             
31674             // from jquery ui (MIT licenced)
31675             var range, node;
31676             var win = this.win;
31677             
31678             if (win.getSelection && win.getSelection().getRangeAt) {
31679                 
31680                 // delete the existing?
31681                 
31682                 this.createRange(this.getSelection()).deleteContents();
31683                 range = win.getSelection().getRangeAt(0);
31684                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31685                 range.insertNode(node);
31686                 range = range.cloneRange();
31687                 range.collapse(false);
31688                  
31689                 win.getSelection().removeAllRanges();
31690                 win.getSelection().addRange(range);
31691                 
31692                 
31693                 
31694             } else if (win.document.selection && win.document.selection.createRange) {
31695                 // no firefox support
31696                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31697                 win.document.selection.createRange().pasteHTML(txt);
31698             
31699             } else {
31700                 // no firefox support
31701                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31702                 this.execCmd('InsertHTML', txt);
31703             } 
31704             this.syncValue();
31705             
31706             this.deferFocus();
31707         }
31708     },
31709  // private
31710     mozKeyPress : function(e){
31711         if(e.ctrlKey){
31712             var c = e.getCharCode(), cmd;
31713           
31714             if(c > 0){
31715                 c = String.fromCharCode(c).toLowerCase();
31716                 switch(c){
31717                     case 'b':
31718                         cmd = 'bold';
31719                         break;
31720                     case 'i':
31721                         cmd = 'italic';
31722                         break;
31723                     
31724                     case 'u':
31725                         cmd = 'underline';
31726                         break;
31727                     
31728                     //case 'v':
31729                       //  this.cleanUpPaste.defer(100, this);
31730                       //  return;
31731                         
31732                 }
31733                 if(cmd){
31734                     
31735                     this.relayCmd(cmd);
31736                     //this.win.focus();
31737                     //this.execCmd(cmd);
31738                     //this.deferFocus();
31739                     e.preventDefault();
31740                 }
31741                 
31742             }
31743         }
31744     },
31745
31746     // private
31747     fixKeys : function(){ // load time branching for fastest keydown performance
31748         
31749         
31750         if(Roo.isIE){
31751             return function(e){
31752                 var k = e.getKey(), r;
31753                 if(k == e.TAB){
31754                     e.stopEvent();
31755                     r = this.doc.selection.createRange();
31756                     if(r){
31757                         r.collapse(true);
31758                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31759                         this.deferFocus();
31760                     }
31761                     return;
31762                 }
31763                 /// this is handled by Roo.htmleditor.KeyEnter
31764                  /*
31765                 if(k == e.ENTER){
31766                     r = this.doc.selection.createRange();
31767                     if(r){
31768                         var target = r.parentElement();
31769                         if(!target || target.tagName.toLowerCase() != 'li'){
31770                             e.stopEvent();
31771                             r.pasteHTML('<br/>');
31772                             r.collapse(false);
31773                             r.select();
31774                         }
31775                     }
31776                 }
31777                 */
31778                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31779                 //    this.cleanUpPaste.defer(100, this);
31780                 //    return;
31781                 //}
31782                 
31783                 
31784             };
31785         }else if(Roo.isOpera){
31786             return function(e){
31787                 var k = e.getKey();
31788                 if(k == e.TAB){
31789                     e.stopEvent();
31790                     this.win.focus();
31791                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31792                     this.deferFocus();
31793                 }
31794                
31795                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31796                 //    this.cleanUpPaste.defer(100, this);
31797                  //   return;
31798                 //}
31799                 
31800             };
31801         }else if(Roo.isSafari){
31802             return function(e){
31803                 var k = e.getKey();
31804                 
31805                 if(k == e.TAB){
31806                     e.stopEvent();
31807                     this.execCmd('InsertText','\t');
31808                     this.deferFocus();
31809                     return;
31810                 }
31811                  this.mozKeyPress(e);
31812                 
31813                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31814                  //   this.cleanUpPaste.defer(100, this);
31815                  //   return;
31816                // }
31817                 
31818              };
31819         }
31820     }(),
31821     
31822     getAllAncestors: function()
31823     {
31824         var p = this.getSelectedNode();
31825         var a = [];
31826         if (!p) {
31827             a.push(p); // push blank onto stack..
31828             p = this.getParentElement();
31829         }
31830         
31831         
31832         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31833             a.push(p);
31834             p = p.parentNode;
31835         }
31836         a.push(this.doc.body);
31837         return a;
31838     },
31839     lastSel : false,
31840     lastSelNode : false,
31841     
31842     
31843     getSelection : function() 
31844     {
31845         this.assignDocWin();
31846         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31847     },
31848     /**
31849      * Select a dom node
31850      * @param {DomElement} node the node to select
31851      */
31852     selectNode : function(node, collapse)
31853     {
31854         var nodeRange = node.ownerDocument.createRange();
31855         try {
31856             nodeRange.selectNode(node);
31857         } catch (e) {
31858             nodeRange.selectNodeContents(node);
31859         }
31860         if (collapse === true) {
31861             nodeRange.collapse(true);
31862         }
31863         //
31864         var s = this.win.getSelection();
31865         s.removeAllRanges();
31866         s.addRange(nodeRange);
31867     },
31868     
31869     getSelectedNode: function() 
31870     {
31871         // this may only work on Gecko!!!
31872         
31873         // should we cache this!!!!
31874         
31875          
31876          
31877         var range = this.createRange(this.getSelection()).cloneRange();
31878         
31879         if (Roo.isIE) {
31880             var parent = range.parentElement();
31881             while (true) {
31882                 var testRange = range.duplicate();
31883                 testRange.moveToElementText(parent);
31884                 if (testRange.inRange(range)) {
31885                     break;
31886                 }
31887                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31888                     break;
31889                 }
31890                 parent = parent.parentElement;
31891             }
31892             return parent;
31893         }
31894         
31895         // is ancestor a text element.
31896         var ac =  range.commonAncestorContainer;
31897         if (ac.nodeType == 3) {
31898             ac = ac.parentNode;
31899         }
31900         
31901         var ar = ac.childNodes;
31902          
31903         var nodes = [];
31904         var other_nodes = [];
31905         var has_other_nodes = false;
31906         for (var i=0;i<ar.length;i++) {
31907             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31908                 continue;
31909             }
31910             // fullly contained node.
31911             
31912             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31913                 nodes.push(ar[i]);
31914                 continue;
31915             }
31916             
31917             // probably selected..
31918             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31919                 other_nodes.push(ar[i]);
31920                 continue;
31921             }
31922             // outer..
31923             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31924                 continue;
31925             }
31926             
31927             
31928             has_other_nodes = true;
31929         }
31930         if (!nodes.length && other_nodes.length) {
31931             nodes= other_nodes;
31932         }
31933         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31934             return false;
31935         }
31936         
31937         return nodes[0];
31938     },
31939     
31940     
31941     createRange: function(sel)
31942     {
31943         // this has strange effects when using with 
31944         // top toolbar - not sure if it's a great idea.
31945         //this.editor.contentWindow.focus();
31946         if (typeof sel != "undefined") {
31947             try {
31948                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31949             } catch(e) {
31950                 return this.doc.createRange();
31951             }
31952         } else {
31953             return this.doc.createRange();
31954         }
31955     },
31956     getParentElement: function()
31957     {
31958         
31959         this.assignDocWin();
31960         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31961         
31962         var range = this.createRange(sel);
31963          
31964         try {
31965             var p = range.commonAncestorContainer;
31966             while (p.nodeType == 3) { // text node
31967                 p = p.parentNode;
31968             }
31969             return p;
31970         } catch (e) {
31971             return null;
31972         }
31973     
31974     },
31975     /***
31976      *
31977      * Range intersection.. the hard stuff...
31978      *  '-1' = before
31979      *  '0' = hits..
31980      *  '1' = after.
31981      *         [ -- selected range --- ]
31982      *   [fail]                        [fail]
31983      *
31984      *    basically..
31985      *      if end is before start or  hits it. fail.
31986      *      if start is after end or hits it fail.
31987      *
31988      *   if either hits (but other is outside. - then it's not 
31989      *   
31990      *    
31991      **/
31992     
31993     
31994     // @see http://www.thismuchiknow.co.uk/?p=64.
31995     rangeIntersectsNode : function(range, node)
31996     {
31997         var nodeRange = node.ownerDocument.createRange();
31998         try {
31999             nodeRange.selectNode(node);
32000         } catch (e) {
32001             nodeRange.selectNodeContents(node);
32002         }
32003     
32004         var rangeStartRange = range.cloneRange();
32005         rangeStartRange.collapse(true);
32006     
32007         var rangeEndRange = range.cloneRange();
32008         rangeEndRange.collapse(false);
32009     
32010         var nodeStartRange = nodeRange.cloneRange();
32011         nodeStartRange.collapse(true);
32012     
32013         var nodeEndRange = nodeRange.cloneRange();
32014         nodeEndRange.collapse(false);
32015     
32016         return rangeStartRange.compareBoundaryPoints(
32017                  Range.START_TO_START, nodeEndRange) == -1 &&
32018                rangeEndRange.compareBoundaryPoints(
32019                  Range.START_TO_START, nodeStartRange) == 1;
32020         
32021          
32022     },
32023     rangeCompareNode : function(range, node)
32024     {
32025         var nodeRange = node.ownerDocument.createRange();
32026         try {
32027             nodeRange.selectNode(node);
32028         } catch (e) {
32029             nodeRange.selectNodeContents(node);
32030         }
32031         
32032         
32033         range.collapse(true);
32034     
32035         nodeRange.collapse(true);
32036      
32037         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32038         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
32039          
32040         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32041         
32042         var nodeIsBefore   =  ss == 1;
32043         var nodeIsAfter    = ee == -1;
32044         
32045         if (nodeIsBefore && nodeIsAfter) {
32046             return 0; // outer
32047         }
32048         if (!nodeIsBefore && nodeIsAfter) {
32049             return 1; //right trailed.
32050         }
32051         
32052         if (nodeIsBefore && !nodeIsAfter) {
32053             return 2;  // left trailed.
32054         }
32055         // fully contined.
32056         return 3;
32057     },
32058  
32059     cleanWordChars : function(input) {// change the chars to hex code
32060         
32061        var swapCodes  = [ 
32062             [    8211, "&#8211;" ], 
32063             [    8212, "&#8212;" ], 
32064             [    8216,  "'" ],  
32065             [    8217, "'" ],  
32066             [    8220, '"' ],  
32067             [    8221, '"' ],  
32068             [    8226, "*" ],  
32069             [    8230, "..." ]
32070         ]; 
32071         var output = input;
32072         Roo.each(swapCodes, function(sw) { 
32073             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32074             
32075             output = output.replace(swapper, sw[1]);
32076         });
32077         
32078         return output;
32079     },
32080     
32081      
32082     
32083         
32084     
32085     cleanUpChild : function (node)
32086     {
32087         
32088         new Roo.htmleditor.FilterComment({node : node});
32089         new Roo.htmleditor.FilterAttributes({
32090                 node : node,
32091                 attrib_black : this.ablack,
32092                 attrib_clean : this.aclean,
32093                 style_white : this.cwhite,
32094                 style_black : this.cblack
32095         });
32096         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32097         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32098          
32099         
32100     },
32101     
32102     /**
32103      * Clean up MS wordisms...
32104      * @deprecated - use filter directly
32105      */
32106     cleanWord : function(node)
32107     {
32108         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32109         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32110         
32111     },
32112    
32113     
32114     /**
32115
32116      * @deprecated - use filters
32117      */
32118     cleanTableWidths : function(node)
32119     {
32120         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32121         
32122  
32123     },
32124     
32125      
32126         
32127     applyBlacklists : function()
32128     {
32129         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
32130         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
32131         
32132         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
32133         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
32134         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
32135         
32136         this.white = [];
32137         this.black = [];
32138         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32139             if (b.indexOf(tag) > -1) {
32140                 return;
32141             }
32142             this.white.push(tag);
32143             
32144         }, this);
32145         
32146         Roo.each(w, function(tag) {
32147             if (b.indexOf(tag) > -1) {
32148                 return;
32149             }
32150             if (this.white.indexOf(tag) > -1) {
32151                 return;
32152             }
32153             this.white.push(tag);
32154             
32155         }, this);
32156         
32157         
32158         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32159             if (w.indexOf(tag) > -1) {
32160                 return;
32161             }
32162             this.black.push(tag);
32163             
32164         }, this);
32165         
32166         Roo.each(b, function(tag) {
32167             if (w.indexOf(tag) > -1) {
32168                 return;
32169             }
32170             if (this.black.indexOf(tag) > -1) {
32171                 return;
32172             }
32173             this.black.push(tag);
32174             
32175         }, this);
32176         
32177         
32178         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
32179         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
32180         
32181         this.cwhite = [];
32182         this.cblack = [];
32183         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32184             if (b.indexOf(tag) > -1) {
32185                 return;
32186             }
32187             this.cwhite.push(tag);
32188             
32189         }, this);
32190         
32191         Roo.each(w, function(tag) {
32192             if (b.indexOf(tag) > -1) {
32193                 return;
32194             }
32195             if (this.cwhite.indexOf(tag) > -1) {
32196                 return;
32197             }
32198             this.cwhite.push(tag);
32199             
32200         }, this);
32201         
32202         
32203         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32204             if (w.indexOf(tag) > -1) {
32205                 return;
32206             }
32207             this.cblack.push(tag);
32208             
32209         }, this);
32210         
32211         Roo.each(b, function(tag) {
32212             if (w.indexOf(tag) > -1) {
32213                 return;
32214             }
32215             if (this.cblack.indexOf(tag) > -1) {
32216                 return;
32217             }
32218             this.cblack.push(tag);
32219             
32220         }, this);
32221     },
32222     
32223     setStylesheets : function(stylesheets)
32224     {
32225         if(typeof(stylesheets) == 'string'){
32226             Roo.get(this.iframe.contentDocument.head).createChild({
32227                 tag : 'link',
32228                 rel : 'stylesheet',
32229                 type : 'text/css',
32230                 href : stylesheets
32231             });
32232             
32233             return;
32234         }
32235         var _this = this;
32236      
32237         Roo.each(stylesheets, function(s) {
32238             if(!s.length){
32239                 return;
32240             }
32241             
32242             Roo.get(_this.iframe.contentDocument.head).createChild({
32243                 tag : 'link',
32244                 rel : 'stylesheet',
32245                 type : 'text/css',
32246                 href : s
32247             });
32248         });
32249
32250         
32251     },
32252     
32253     
32254     updateLanguage : function()
32255     {
32256         if (!this.iframe || !this.iframe.contentDocument) {
32257             return;
32258         }
32259         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32260     },
32261     
32262     
32263     removeStylesheets : function()
32264     {
32265         var _this = this;
32266         
32267         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32268             s.remove();
32269         });
32270     },
32271     
32272     setStyle : function(style)
32273     {
32274         Roo.get(this.iframe.contentDocument.head).createChild({
32275             tag : 'style',
32276             type : 'text/css',
32277             html : style
32278         });
32279
32280         return;
32281     }
32282     
32283     // hide stuff that is not compatible
32284     /**
32285      * @event blur
32286      * @hide
32287      */
32288     /**
32289      * @event change
32290      * @hide
32291      */
32292     /**
32293      * @event focus
32294      * @hide
32295      */
32296     /**
32297      * @event specialkey
32298      * @hide
32299      */
32300     /**
32301      * @cfg {String} fieldClass @hide
32302      */
32303     /**
32304      * @cfg {String} focusClass @hide
32305      */
32306     /**
32307      * @cfg {String} autoCreate @hide
32308      */
32309     /**
32310      * @cfg {String} inputType @hide
32311      */
32312     /**
32313      * @cfg {String} invalidClass @hide
32314      */
32315     /**
32316      * @cfg {String} invalidText @hide
32317      */
32318     /**
32319      * @cfg {String} msgFx @hide
32320      */
32321     /**
32322      * @cfg {String} validateOnBlur @hide
32323      */
32324 });
32325
32326 Roo.HtmlEditorCore.white = [
32327         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32328         
32329        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
32330        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
32331        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
32332        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
32333        'TABLE',   'UL',         'XMP', 
32334        
32335        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
32336       'THEAD',   'TR', 
32337      
32338       'DIR', 'MENU', 'OL', 'UL', 'DL',
32339        
32340       'EMBED',  'OBJECT'
32341 ];
32342
32343
32344 Roo.HtmlEditorCore.black = [
32345     //    'embed',  'object', // enable - backend responsiblity to clean thiese
32346         'APPLET', // 
32347         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
32348         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
32349         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
32350         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
32351         //'FONT' // CLEAN LATER..
32352         'COLGROUP', 'COL'   // messy tables.
32353         
32354         
32355 ];
32356 Roo.HtmlEditorCore.clean = [ // ?? needed???
32357      'SCRIPT', 'STYLE', 'TITLE', 'XML'
32358 ];
32359 Roo.HtmlEditorCore.tag_remove = [
32360     'FONT', 'TBODY'  
32361 ];
32362 // attributes..
32363
32364 Roo.HtmlEditorCore.ablack = [
32365     'on'
32366 ];
32367     
32368 Roo.HtmlEditorCore.aclean = [ 
32369     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
32370 ];
32371
32372 // protocols..
32373 Roo.HtmlEditorCore.pwhite= [
32374         'http',  'https',  'mailto'
32375 ];
32376
32377 // white listed style attributes.
32378 Roo.HtmlEditorCore.cwhite= [
32379       //  'text-align', /// default is to allow most things..
32380       
32381          
32382 //        'font-size'//??
32383 ];
32384
32385 // black listed style attributes.
32386 Roo.HtmlEditorCore.cblack= [
32387       //  'font-size' -- this can be set by the project 
32388 ];
32389
32390
32391
32392
32393     /*
32394  * - LGPL
32395  *
32396  * HtmlEditor
32397  * 
32398  */
32399
32400 /**
32401  * @class Roo.bootstrap.form.HtmlEditor
32402  * @extends Roo.bootstrap.form.TextArea
32403  * Bootstrap HtmlEditor class
32404
32405  * @constructor
32406  * Create a new HtmlEditor
32407  * @param {Object} config The config object
32408  */
32409
32410 Roo.bootstrap.form.HtmlEditor = function(config){
32411     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32412     if (!this.toolbars) {
32413         this.toolbars = [];
32414     }
32415     
32416     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32417     this.addEvents({
32418             /**
32419              * @event initialize
32420              * Fires when the editor is fully initialized (including the iframe)
32421              * @param {HtmlEditor} this
32422              */
32423             initialize: true,
32424             /**
32425              * @event activate
32426              * Fires when the editor is first receives the focus. Any insertion must wait
32427              * until after this event.
32428              * @param {HtmlEditor} this
32429              */
32430             activate: true,
32431              /**
32432              * @event beforesync
32433              * Fires before the textarea is updated with content from the editor iframe. Return false
32434              * to cancel the sync.
32435              * @param {HtmlEditor} this
32436              * @param {String} html
32437              */
32438             beforesync: true,
32439              /**
32440              * @event beforepush
32441              * Fires before the iframe editor is updated with content from the textarea. Return false
32442              * to cancel the push.
32443              * @param {HtmlEditor} this
32444              * @param {String} html
32445              */
32446             beforepush: true,
32447              /**
32448              * @event sync
32449              * Fires when the textarea is updated with content from the editor iframe.
32450              * @param {HtmlEditor} this
32451              * @param {String} html
32452              */
32453             sync: true,
32454              /**
32455              * @event push
32456              * Fires when the iframe editor is updated with content from the textarea.
32457              * @param {HtmlEditor} this
32458              * @param {String} html
32459              */
32460             push: true,
32461              /**
32462              * @event editmodechange
32463              * Fires when the editor switches edit modes
32464              * @param {HtmlEditor} this
32465              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32466              */
32467             editmodechange: true,
32468             /**
32469              * @event editorevent
32470              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32471              * @param {HtmlEditor} this
32472              */
32473             editorevent: true,
32474             /**
32475              * @event firstfocus
32476              * Fires when on first focus - needed by toolbars..
32477              * @param {HtmlEditor} this
32478              */
32479             firstfocus: true,
32480             /**
32481              * @event autosave
32482              * Auto save the htmlEditor value as a file into Events
32483              * @param {HtmlEditor} this
32484              */
32485             autosave: true,
32486             /**
32487              * @event savedpreview
32488              * preview the saved version of htmlEditor
32489              * @param {HtmlEditor} this
32490              */
32491             savedpreview: true
32492         });
32493 };
32494
32495
32496 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
32497     
32498     
32499       /**
32500      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
32501      */
32502     toolbars : false,
32503     
32504      /**
32505     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32506     */
32507     btns : [],
32508    
32509      /**
32510      * @cfg {String} resize  (none|both|horizontal|vertical) - css resize of element
32511      */
32512     resize : 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          
32592         this.createToolbar(this);
32593        
32594         
32595           
32596         
32597     },
32598
32599     // private
32600     onResize : function(w, h)
32601     {
32602         Roo.log('resize: ' +w + ',' + h );
32603         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32604         var ew = false;
32605         var eh = false;
32606         
32607         if(this.inputEl() ){
32608             if(typeof w == 'number'){
32609                 var aw = w - this.wrap.getFrameWidth('lr');
32610                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32611                 ew = aw;
32612             }
32613             if(typeof h == 'number'){
32614                  var tbh = -11;  // fixme it needs to tool bar size!
32615                 for (var i =0; i < this.toolbars.length;i++) {
32616                     // fixme - ask toolbars for heights?
32617                     tbh += this.toolbars[i].el.getHeight();
32618                     //if (this.toolbars[i].footer) {
32619                     //    tbh += this.toolbars[i].footer.el.getHeight();
32620                     //}
32621                 }
32622               
32623                 
32624                 
32625                 
32626                 
32627                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32628                 ah -= 5; // knock a few pixes off for look..
32629                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32630                 var eh = ah;
32631             }
32632         }
32633         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32634         this.editorcore.onResize(ew,eh);
32635         
32636     },
32637
32638     /**
32639      * Toggles the editor between standard and source edit mode.
32640      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32641      */
32642     toggleSourceEdit : function(sourceEditMode)
32643     {
32644         this.editorcore.toggleSourceEdit(sourceEditMode);
32645         
32646         if(this.editorcore.sourceEditMode){
32647             Roo.log('editor - showing textarea');
32648             
32649 //            Roo.log('in');
32650 //            Roo.log(this.syncValue());
32651             this.syncValue();
32652             this.inputEl().removeClass(['hide', 'x-hidden']);
32653             this.inputEl().dom.removeAttribute('tabIndex');
32654             this.inputEl().focus();
32655         }else{
32656             Roo.log('editor - hiding textarea');
32657 //            Roo.log('out')
32658 //            Roo.log(this.pushValue()); 
32659             this.pushValue();
32660             
32661             this.inputEl().addClass(['hide', 'x-hidden']);
32662             this.inputEl().dom.setAttribute('tabIndex', -1);
32663             //this.deferFocus();
32664         }
32665          
32666         //if(this.resizable){
32667         //    this.setSize(this.wrap.getSize());
32668         //}
32669         
32670         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32671     },
32672  
32673     // private (for BoxComponent)
32674     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32675
32676     // private (for BoxComponent)
32677     getResizeEl : function(){
32678         return this.wrap;
32679     },
32680
32681     // private (for BoxComponent)
32682     getPositionEl : function(){
32683         return this.wrap;
32684     },
32685
32686     // private
32687     initEvents : function(){
32688         this.originalValue = this.getValue();
32689     },
32690
32691 //    /**
32692 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32693 //     * @method
32694 //     */
32695 //    markInvalid : Roo.emptyFn,
32696 //    /**
32697 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32698 //     * @method
32699 //     */
32700 //    clearInvalid : Roo.emptyFn,
32701
32702     setValue : function(v){
32703         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32704         this.editorcore.pushValue();
32705     },
32706
32707      
32708     // private
32709     deferFocus : function(){
32710         this.focus.defer(10, this);
32711     },
32712
32713     // doc'ed in Field
32714     focus : function(){
32715         this.editorcore.focus();
32716         
32717     },
32718       
32719
32720     // private
32721     onDestroy : function(){
32722         
32723         
32724         
32725         if(this.rendered){
32726             
32727             for (var i =0; i < this.toolbars.length;i++) {
32728                 // fixme - ask toolbars for heights?
32729                 this.toolbars[i].onDestroy();
32730             }
32731             
32732             this.wrap.dom.innerHTML = '';
32733             this.wrap.remove();
32734         }
32735     },
32736
32737     // private
32738     onFirstFocus : function(){
32739         //Roo.log("onFirstFocus");
32740         this.editorcore.onFirstFocus();
32741          for (var i =0; i < this.toolbars.length;i++) {
32742             this.toolbars[i].onFirstFocus();
32743         }
32744         
32745     },
32746     
32747     // private
32748     syncValue : function()
32749     {   
32750         this.editorcore.syncValue();
32751     },
32752     
32753     pushValue : function()
32754     {   
32755         this.editorcore.pushValue();
32756     }
32757      
32758     
32759     // hide stuff that is not compatible
32760     /**
32761      * @event blur
32762      * @hide
32763      */
32764     /**
32765      * @event change
32766      * @hide
32767      */
32768     /**
32769      * @event focus
32770      * @hide
32771      */
32772     /**
32773      * @event specialkey
32774      * @hide
32775      */
32776     /**
32777      * @cfg {String} fieldClass @hide
32778      */
32779     /**
32780      * @cfg {String} focusClass @hide
32781      */
32782     /**
32783      * @cfg {String} autoCreate @hide
32784      */
32785     /**
32786      * @cfg {String} inputType @hide
32787      */
32788      
32789     /**
32790      * @cfg {String} invalidText @hide
32791      */
32792     /**
32793      * @cfg {String} msgFx @hide
32794      */
32795     /**
32796      * @cfg {String} validateOnBlur @hide
32797      */
32798 });
32799  
32800     
32801    
32802    
32803    
32804       
32805 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32806 /**
32807  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32808  * @parent Roo.bootstrap.form.HtmlEditor
32809  * @extends Roo.bootstrap.nav.Simplebar
32810  * Basic Toolbar
32811  * 
32812  * @example
32813  * Usage:
32814  *
32815  new Roo.bootstrap.form.HtmlEditor({
32816     ....
32817     toolbars : [
32818         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32819             disable : { fonts: 1 , format: 1, ..., ... , ...],
32820             btns : [ .... ]
32821         })
32822     }
32823      
32824  * 
32825  * @cfg {Object} disable List of elements to disable..
32826  * @cfg {Array} btns List of additional buttons.
32827  * 
32828  * 
32829  * NEEDS Extra CSS? 
32830  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32831  */
32832  
32833 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32834 {
32835     
32836     Roo.apply(this, config);
32837     
32838     // default disabled, based on 'good practice'..
32839     this.disable = this.disable || {};
32840     Roo.applyIf(this.disable, {
32841         fontSize : true,
32842         colors : true,
32843         specialElements : true
32844     });
32845     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32846     
32847     this.editor = config.editor;
32848     this.editorcore = config.editor.editorcore;
32849     
32850     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32851     
32852     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32853     // dont call parent... till later.
32854 }
32855 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
32856      
32857     bar : true,
32858     
32859     editor : false,
32860     editorcore : false,
32861     
32862     
32863     formats : [
32864         "p" ,  
32865         "h1","h2","h3","h4","h5","h6", 
32866         "pre", "code", 
32867         "abbr", "acronym", "address", "cite", "samp", "var",
32868         'div','span'
32869     ],
32870     
32871     onRender : function(ct, position)
32872     {
32873        // Roo.log("Call onRender: " + this.xtype);
32874         
32875        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32876        Roo.log(this.el);
32877        this.el.dom.style.marginBottom = '0';
32878        var _this = this;
32879        var editorcore = this.editorcore;
32880        var editor= this.editor;
32881        
32882        var children = [];
32883        var btn = function(id,cmd , toggle, handler, html){
32884        
32885             var  event = toggle ? 'toggle' : 'click';
32886        
32887             var a = {
32888                 size : 'sm',
32889                 xtype: 'Button',
32890                 xns: Roo.bootstrap,
32891                 //glyphicon : id,
32892                 fa: id,
32893                 cmd : id || cmd,
32894                 enableToggle:toggle !== false,
32895                 html : html || '',
32896                 pressed : toggle ? false : null,
32897                 listeners : {}
32898             };
32899             a.listeners[toggle ? 'toggle' : 'click'] = function() {
32900                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
32901             };
32902             children.push(a);
32903             return a;
32904        }
32905        
32906     //    var cb_box = function...
32907         
32908         var style = {
32909                 xtype: 'Button',
32910                 size : 'sm',
32911                 xns: Roo.bootstrap,
32912                 fa : 'font',
32913                 //html : 'submit'
32914                 menu : {
32915                     xtype: 'Menu',
32916                     xns: Roo.bootstrap,
32917                     items:  []
32918                 }
32919         };
32920         Roo.each(this.formats, function(f) {
32921             style.menu.items.push({
32922                 xtype :'MenuItem',
32923                 xns: Roo.bootstrap,
32924                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32925                 tagname : f,
32926                 listeners : {
32927                     click : function()
32928                     {
32929                         editorcore.insertTag(this.tagname);
32930                         editor.focus();
32931                     }
32932                 }
32933                 
32934             });
32935         });
32936         children.push(style);   
32937         
32938         btn('bold',false,true);
32939         btn('italic',false,true);
32940         btn('align-left', 'justifyleft',true);
32941         btn('align-center', 'justifycenter',true);
32942         btn('align-right' , 'justifyright',true);
32943         btn('link', false, false, function(btn) {
32944             //Roo.log("create link?");
32945             var url = prompt(this.createLinkText, this.defaultLinkValue);
32946             if(url && url != 'http:/'+'/'){
32947                 this.editorcore.relayCmd('createlink', url);
32948             }
32949         }),
32950         btn('list','insertunorderedlist',true);
32951         btn('pencil', false,true, function(btn){
32952                 Roo.log(this);
32953                 this.toggleSourceEdit(btn.pressed);
32954         });
32955         
32956         if (this.editor.btns.length > 0) {
32957             for (var i = 0; i<this.editor.btns.length; i++) {
32958                 children.push(this.editor.btns[i]);
32959             }
32960         }
32961         
32962         /*
32963         var cog = {
32964                 xtype: 'Button',
32965                 size : 'sm',
32966                 xns: Roo.bootstrap,
32967                 glyphicon : 'cog',
32968                 //html : 'submit'
32969                 menu : {
32970                     xtype: 'Menu',
32971                     xns: Roo.bootstrap,
32972                     items:  []
32973                 }
32974         };
32975         
32976         cog.menu.items.push({
32977             xtype :'MenuItem',
32978             xns: Roo.bootstrap,
32979             html : Clean styles,
32980             tagname : f,
32981             listeners : {
32982                 click : function()
32983                 {
32984                     editorcore.insertTag(this.tagname);
32985                     editor.focus();
32986                 }
32987             }
32988             
32989         });
32990        */
32991         
32992          
32993        this.xtype = 'NavSimplebar';
32994         
32995         for(var i=0;i< children.length;i++) {
32996             
32997             this.buttons.add(this.addxtypeChild(children[i]));
32998             
32999         }
33000         
33001         editor.on('editorevent', this.updateToolbar, this);
33002     },
33003     onBtnClick : function(id)
33004     {
33005        this.editorcore.relayCmd(id);
33006        this.editorcore.focus();
33007     },
33008     
33009     /**
33010      * Protected method that will not generally be called directly. It triggers
33011      * a toolbar update by reading the markup state of the current selection in the editor.
33012      */
33013     updateToolbar: function(){
33014
33015         if(!this.editorcore.activated){
33016             this.editor.onFirstFocus(); // is this neeed?
33017             return;
33018         }
33019
33020         var btns = this.buttons; 
33021         var doc = this.editorcore.doc;
33022         btns.get('bold').setActive(doc.queryCommandState('bold'));
33023         btns.get('italic').setActive(doc.queryCommandState('italic'));
33024         //btns.get('underline').setActive(doc.queryCommandState('underline'));
33025         
33026         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
33027         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
33028         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
33029         
33030         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
33031         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
33032          /*
33033         
33034         var ans = this.editorcore.getAllAncestors();
33035         if (this.formatCombo) {
33036             
33037             
33038             var store = this.formatCombo.store;
33039             this.formatCombo.setValue("");
33040             for (var i =0; i < ans.length;i++) {
33041                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
33042                     // select it..
33043                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
33044                     break;
33045                 }
33046             }
33047         }
33048         
33049         
33050         
33051         // hides menus... - so this cant be on a menu...
33052         Roo.bootstrap.MenuMgr.hideAll();
33053         */
33054         Roo.bootstrap.menu.Manager.hideAll();
33055         //this.editorsyncValue();
33056     },
33057     onFirstFocus: function() {
33058         this.buttons.each(function(item){
33059            item.enable();
33060         });
33061     },
33062     toggleSourceEdit : function(sourceEditMode){
33063         
33064           
33065         if(sourceEditMode){
33066             Roo.log("disabling buttons");
33067            this.buttons.each( function(item){
33068                 if(item.cmd != 'pencil'){
33069                     item.disable();
33070                 }
33071             });
33072           
33073         }else{
33074             Roo.log("enabling buttons");
33075             if(this.editorcore.initialized){
33076                 this.buttons.each( function(item){
33077                     item.enable();
33078                 });
33079             }
33080             
33081         }
33082         Roo.log("calling toggole on editor");
33083         // tell the editor that it's been pressed..
33084         this.editor.toggleSourceEdit(sourceEditMode);
33085        
33086     }
33087 });
33088
33089
33090
33091
33092  
33093 /*
33094  * - LGPL
33095  */
33096
33097 /**
33098  * @class Roo.bootstrap.form.Markdown
33099  * @extends Roo.bootstrap.form.TextArea
33100  * Bootstrap Showdown editable area
33101  * @cfg {string} content
33102  * 
33103  * @constructor
33104  * Create a new Showdown
33105  */
33106
33107 Roo.bootstrap.form.Markdown = function(config){
33108     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33109    
33110 };
33111
33112 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
33113     
33114     editing :false,
33115     
33116     initEvents : function()
33117     {
33118         
33119         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33120         this.markdownEl = this.el.createChild({
33121             cls : 'roo-markdown-area'
33122         });
33123         this.inputEl().addClass('d-none');
33124         if (this.getValue() == '') {
33125             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33126             
33127         } else {
33128             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33129         }
33130         this.markdownEl.on('click', this.toggleTextEdit, this);
33131         this.on('blur', this.toggleTextEdit, this);
33132         this.on('specialkey', this.resizeTextArea, this);
33133     },
33134     
33135     toggleTextEdit : function()
33136     {
33137         var sh = this.markdownEl.getHeight();
33138         this.inputEl().addClass('d-none');
33139         this.markdownEl.addClass('d-none');
33140         if (!this.editing) {
33141             // show editor?
33142             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33143             this.inputEl().removeClass('d-none');
33144             this.inputEl().focus();
33145             this.editing = true;
33146             return;
33147         }
33148         // show showdown...
33149         this.updateMarkdown();
33150         this.markdownEl.removeClass('d-none');
33151         this.editing = false;
33152         return;
33153     },
33154     updateMarkdown : function()
33155     {
33156         if (this.getValue() == '') {
33157             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33158             return;
33159         }
33160  
33161         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33162     },
33163     
33164     resizeTextArea: function () {
33165         
33166         var sh = 100;
33167         Roo.log([sh, this.getValue().split("\n").length * 30]);
33168         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33169     },
33170     setValue : function(val)
33171     {
33172         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33173         if (!this.editing) {
33174             this.updateMarkdown();
33175         }
33176         
33177     },
33178     focus : function()
33179     {
33180         if (!this.editing) {
33181             this.toggleTextEdit();
33182         }
33183         
33184     }
33185
33186
33187 });/*
33188  * Based on:
33189  * Ext JS Library 1.1.1
33190  * Copyright(c) 2006-2007, Ext JS, LLC.
33191  *
33192  * Originally Released Under LGPL - original licence link has changed is not relivant.
33193  *
33194  * Fork - LGPL
33195  * <script type="text/javascript">
33196  */
33197  
33198 /**
33199  * @class Roo.bootstrap.PagingToolbar
33200  * @extends Roo.bootstrap.nav.Simplebar
33201  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33202  * @constructor
33203  * Create a new PagingToolbar
33204  * @param {Object} config The config object
33205  * @param {Roo.data.Store} store
33206  */
33207 Roo.bootstrap.PagingToolbar = function(config)
33208 {
33209     // old args format still supported... - xtype is prefered..
33210         // created from xtype...
33211     
33212     this.ds = config.dataSource;
33213     
33214     if (config.store && !this.ds) {
33215         this.store= Roo.factory(config.store, Roo.data);
33216         this.ds = this.store;
33217         this.ds.xmodule = this.xmodule || false;
33218     }
33219     
33220     this.toolbarItems = [];
33221     if (config.items) {
33222         this.toolbarItems = config.items;
33223     }
33224     
33225     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33226     
33227     this.cursor = 0;
33228     
33229     if (this.ds) { 
33230         this.bind(this.ds);
33231     }
33232     
33233     if (Roo.bootstrap.version == 4) {
33234         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33235     } else {
33236         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33237     }
33238     
33239 };
33240
33241 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33242     /**
33243      * @cfg {Roo.bootstrap.Button} buttons[]
33244      * Buttons for the toolbar
33245      */
33246      /**
33247      * @cfg {Roo.data.Store} store
33248      * The underlying data store providing the paged data
33249      */
33250     /**
33251      * @cfg {String/HTMLElement/Element} container
33252      * container The id or element that will contain the toolbar
33253      */
33254     /**
33255      * @cfg {Boolean} displayInfo
33256      * True to display the displayMsg (defaults to false)
33257      */
33258     /**
33259      * @cfg {Number} pageSize
33260      * The number of records to display per page (defaults to 20)
33261      */
33262     pageSize: 20,
33263     /**
33264      * @cfg {String} displayMsg
33265      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33266      */
33267     displayMsg : 'Displaying {0} - {1} of {2}',
33268     /**
33269      * @cfg {String} emptyMsg
33270      * The message to display when no records are found (defaults to "No data to display")
33271      */
33272     emptyMsg : 'No data to display',
33273     /**
33274      * Customizable piece of the default paging text (defaults to "Page")
33275      * @type String
33276      */
33277     beforePageText : "Page",
33278     /**
33279      * Customizable piece of the default paging text (defaults to "of %0")
33280      * @type String
33281      */
33282     afterPageText : "of {0}",
33283     /**
33284      * Customizable piece of the default paging text (defaults to "First Page")
33285      * @type String
33286      */
33287     firstText : "First Page",
33288     /**
33289      * Customizable piece of the default paging text (defaults to "Previous Page")
33290      * @type String
33291      */
33292     prevText : "Previous Page",
33293     /**
33294      * Customizable piece of the default paging text (defaults to "Next Page")
33295      * @type String
33296      */
33297     nextText : "Next Page",
33298     /**
33299      * Customizable piece of the default paging text (defaults to "Last Page")
33300      * @type String
33301      */
33302     lastText : "Last Page",
33303     /**
33304      * Customizable piece of the default paging text (defaults to "Refresh")
33305      * @type String
33306      */
33307     refreshText : "Refresh",
33308
33309     buttons : false,
33310     // private
33311     onRender : function(ct, position) 
33312     {
33313         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33314         this.navgroup.parentId = this.id;
33315         this.navgroup.onRender(this.el, null);
33316         // add the buttons to the navgroup
33317         
33318         if(this.displayInfo){
33319             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33320             this.displayEl = this.el.select('.x-paging-info', true).first();
33321 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33322 //            this.displayEl = navel.el.select('span',true).first();
33323         }
33324         
33325         var _this = this;
33326         
33327         if(this.buttons){
33328             Roo.each(_this.buttons, function(e){ // this might need to use render????
33329                Roo.factory(e).render(_this.el);
33330             });
33331         }
33332             
33333         Roo.each(_this.toolbarItems, function(e) {
33334             _this.navgroup.addItem(e);
33335         });
33336         
33337         
33338         this.first = this.navgroup.addItem({
33339             tooltip: this.firstText,
33340             cls: "prev btn-outline-secondary",
33341             html : ' <i class="fa fa-step-backward"></i>',
33342             disabled: true,
33343             preventDefault: true,
33344             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33345         });
33346         
33347         this.prev =  this.navgroup.addItem({
33348             tooltip: this.prevText,
33349             cls: "prev btn-outline-secondary",
33350             html : ' <i class="fa fa-backward"></i>',
33351             disabled: true,
33352             preventDefault: true,
33353             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
33354         });
33355     //this.addSeparator();
33356         
33357         
33358         var field = this.navgroup.addItem( {
33359             tagtype : 'span',
33360             cls : 'x-paging-position  btn-outline-secondary',
33361              disabled: true,
33362             html : this.beforePageText  +
33363                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33364                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
33365          } ); //?? escaped?
33366         
33367         this.field = field.el.select('input', true).first();
33368         this.field.on("keydown", this.onPagingKeydown, this);
33369         this.field.on("focus", function(){this.dom.select();});
33370     
33371     
33372         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
33373         //this.field.setHeight(18);
33374         //this.addSeparator();
33375         this.next = this.navgroup.addItem({
33376             tooltip: this.nextText,
33377             cls: "next btn-outline-secondary",
33378             html : ' <i class="fa fa-forward"></i>',
33379             disabled: true,
33380             preventDefault: true,
33381             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
33382         });
33383         this.last = this.navgroup.addItem({
33384             tooltip: this.lastText,
33385             html : ' <i class="fa fa-step-forward"></i>',
33386             cls: "next btn-outline-secondary",
33387             disabled: true,
33388             preventDefault: true,
33389             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
33390         });
33391     //this.addSeparator();
33392         this.loading = this.navgroup.addItem({
33393             tooltip: this.refreshText,
33394             cls: "btn-outline-secondary",
33395             html : ' <i class="fa fa-refresh"></i>',
33396             preventDefault: true,
33397             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33398         });
33399         
33400     },
33401
33402     // private
33403     updateInfo : function(){
33404         if(this.displayEl){
33405             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33406             var msg = count == 0 ?
33407                 this.emptyMsg :
33408                 String.format(
33409                     this.displayMsg,
33410                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
33411                 );
33412             this.displayEl.update(msg);
33413         }
33414     },
33415
33416     // private
33417     onLoad : function(ds, r, o)
33418     {
33419         this.cursor = o.params && o.params.start ? o.params.start : 0;
33420         
33421         var d = this.getPageData(),
33422             ap = d.activePage,
33423             ps = d.pages;
33424         
33425         
33426         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33427         this.field.dom.value = ap;
33428         this.first.setDisabled(ap == 1);
33429         this.prev.setDisabled(ap == 1);
33430         this.next.setDisabled(ap == ps);
33431         this.last.setDisabled(ap == ps);
33432         this.loading.enable();
33433         this.updateInfo();
33434     },
33435
33436     // private
33437     getPageData : function(){
33438         var total = this.ds.getTotalCount();
33439         return {
33440             total : total,
33441             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33442             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33443         };
33444     },
33445
33446     // private
33447     onLoadError : function(proxy, o){
33448         this.loading.enable();
33449         if (this.ds.events.loadexception.listeners.length  < 2) {
33450             // nothing has been assigned to loadexception except this...
33451             // so 
33452             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33453
33454         }
33455     },
33456
33457     // private
33458     onPagingKeydown : function(e){
33459         var k = e.getKey();
33460         var d = this.getPageData();
33461         if(k == e.RETURN){
33462             var v = this.field.dom.value, pageNum;
33463             if(!v || isNaN(pageNum = parseInt(v, 10))){
33464                 this.field.dom.value = d.activePage;
33465                 return;
33466             }
33467             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33468             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33469             e.stopEvent();
33470         }
33471         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))
33472         {
33473           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33474           this.field.dom.value = pageNum;
33475           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33476           e.stopEvent();
33477         }
33478         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33479         {
33480           var v = this.field.dom.value, pageNum; 
33481           var increment = (e.shiftKey) ? 10 : 1;
33482           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33483                 increment *= -1;
33484           }
33485           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33486             this.field.dom.value = d.activePage;
33487             return;
33488           }
33489           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33490           {
33491             this.field.dom.value = parseInt(v, 10) + increment;
33492             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33493             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33494           }
33495           e.stopEvent();
33496         }
33497     },
33498
33499     // private
33500     beforeLoad : function(){
33501         if(this.loading){
33502             this.loading.disable();
33503         }
33504     },
33505
33506     // private
33507     onClick : function(which){
33508         
33509         var ds = this.ds;
33510         if (!ds) {
33511             return;
33512         }
33513         
33514         switch(which){
33515             case "first":
33516                 ds.load({params:{start: 0, limit: this.pageSize}});
33517             break;
33518             case "prev":
33519                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33520             break;
33521             case "next":
33522                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33523             break;
33524             case "last":
33525                 var total = ds.getTotalCount();
33526                 var extra = total % this.pageSize;
33527                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33528                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33529             break;
33530             case "refresh":
33531                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33532             break;
33533         }
33534     },
33535
33536     /**
33537      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33538      * @param {Roo.data.Store} store The data store to unbind
33539      */
33540     unbind : function(ds){
33541         ds.un("beforeload", this.beforeLoad, this);
33542         ds.un("load", this.onLoad, this);
33543         ds.un("loadexception", this.onLoadError, this);
33544         ds.un("remove", this.updateInfo, this);
33545         ds.un("add", this.updateInfo, this);
33546         this.ds = undefined;
33547     },
33548
33549     /**
33550      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33551      * @param {Roo.data.Store} store The data store to bind
33552      */
33553     bind : function(ds){
33554         ds.on("beforeload", this.beforeLoad, this);
33555         ds.on("load", this.onLoad, this);
33556         ds.on("loadexception", this.onLoadError, this);
33557         ds.on("remove", this.updateInfo, this);
33558         ds.on("add", this.updateInfo, this);
33559         this.ds = ds;
33560     }
33561 });/*
33562  * - LGPL
33563  *
33564  * element
33565  * 
33566  */
33567
33568 /**
33569  * @class Roo.bootstrap.MessageBar
33570  * @extends Roo.bootstrap.Component
33571  * Bootstrap MessageBar class
33572  * @cfg {String} html contents of the MessageBar
33573  * @cfg {String} weight (info | success | warning | danger) default info
33574  * @cfg {String} beforeClass insert the bar before the given class
33575  * @cfg {Boolean} closable (true | false) default false
33576  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33577  * 
33578  * @constructor
33579  * Create a new Element
33580  * @param {Object} config The config object
33581  */
33582
33583 Roo.bootstrap.MessageBar = function(config){
33584     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33585 };
33586
33587 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33588     
33589     html: '',
33590     weight: 'info',
33591     closable: false,
33592     fixed: false,
33593     beforeClass: 'bootstrap-sticky-wrap',
33594     
33595     getAutoCreate : function(){
33596         
33597         var cfg = {
33598             tag: 'div',
33599             cls: 'alert alert-dismissable alert-' + this.weight,
33600             cn: [
33601                 {
33602                     tag: 'span',
33603                     cls: 'message',
33604                     html: this.html || ''
33605                 }
33606             ]
33607         };
33608         
33609         if(this.fixed){
33610             cfg.cls += ' alert-messages-fixed';
33611         }
33612         
33613         if(this.closable){
33614             cfg.cn.push({
33615                 tag: 'button',
33616                 cls: 'close',
33617                 html: 'x'
33618             });
33619         }
33620         
33621         return cfg;
33622     },
33623     
33624     onRender : function(ct, position)
33625     {
33626         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33627         
33628         if(!this.el){
33629             var cfg = Roo.apply({},  this.getAutoCreate());
33630             cfg.id = Roo.id();
33631             
33632             if (this.cls) {
33633                 cfg.cls += ' ' + this.cls;
33634             }
33635             if (this.style) {
33636                 cfg.style = this.style;
33637             }
33638             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33639             
33640             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33641         }
33642         
33643         this.el.select('>button.close').on('click', this.hide, this);
33644         
33645     },
33646     
33647     show : function()
33648     {
33649         if (!this.rendered) {
33650             this.render();
33651         }
33652         
33653         this.el.show();
33654         
33655         this.fireEvent('show', this);
33656         
33657     },
33658     
33659     hide : function()
33660     {
33661         if (!this.rendered) {
33662             this.render();
33663         }
33664         
33665         this.el.hide();
33666         
33667         this.fireEvent('hide', this);
33668     },
33669     
33670     update : function()
33671     {
33672 //        var e = this.el.dom.firstChild;
33673 //        
33674 //        if(this.closable){
33675 //            e = e.nextSibling;
33676 //        }
33677 //        
33678 //        e.data = this.html || '';
33679
33680         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33681     }
33682    
33683 });
33684
33685  
33686
33687      /*
33688  * - LGPL
33689  *
33690  * Graph
33691  * 
33692  */
33693
33694
33695 /**
33696  * @class Roo.bootstrap.Graph
33697  * @extends Roo.bootstrap.Component
33698  * Bootstrap Graph class
33699 > Prameters
33700  -sm {number} sm 4
33701  -md {number} md 5
33702  @cfg {String} graphtype  bar | vbar | pie
33703  @cfg {number} g_x coodinator | centre x (pie)
33704  @cfg {number} g_y coodinator | centre y (pie)
33705  @cfg {number} g_r radius (pie)
33706  @cfg {number} g_height height of the chart (respected by all elements in the set)
33707  @cfg {number} g_width width of the chart (respected by all elements in the set)
33708  @cfg {Object} title The title of the chart
33709     
33710  -{Array}  values
33711  -opts (object) options for the chart 
33712      o {
33713      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33714      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33715      o vgutter (number)
33716      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.
33717      o stacked (boolean) whether or not to tread values as in a stacked bar chart
33718      o to
33719      o stretch (boolean)
33720      o }
33721  -opts (object) options for the pie
33722      o{
33723      o cut
33724      o startAngle (number)
33725      o endAngle (number)
33726      } 
33727  *
33728  * @constructor
33729  * Create a new Input
33730  * @param {Object} config The config object
33731  */
33732
33733 Roo.bootstrap.Graph = function(config){
33734     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33735     
33736     this.addEvents({
33737         // img events
33738         /**
33739          * @event click
33740          * The img click event for the img.
33741          * @param {Roo.EventObject} e
33742          */
33743         "click" : true
33744     });
33745 };
33746
33747 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
33748     
33749     sm: 4,
33750     md: 5,
33751     graphtype: 'bar',
33752     g_height: 250,
33753     g_width: 400,
33754     g_x: 50,
33755     g_y: 50,
33756     g_r: 30,
33757     opts:{
33758         //g_colors: this.colors,
33759         g_type: 'soft',
33760         g_gutter: '20%'
33761
33762     },
33763     title : false,
33764
33765     getAutoCreate : function(){
33766         
33767         var cfg = {
33768             tag: 'div',
33769             html : null
33770         };
33771         
33772         
33773         return  cfg;
33774     },
33775
33776     onRender : function(ct,position){
33777         
33778         
33779         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33780         
33781         if (typeof(Raphael) == 'undefined') {
33782             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33783             return;
33784         }
33785         
33786         this.raphael = Raphael(this.el.dom);
33787         
33788                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33789                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33790                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33791                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33792                 /*
33793                 r.text(160, 10, "Single Series Chart").attr(txtattr);
33794                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33795                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33796                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33797                 
33798                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33799                 r.barchart(330, 10, 300, 220, data1);
33800                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33801                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33802                 */
33803                 
33804                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33805                 // r.barchart(30, 30, 560, 250,  xdata, {
33806                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33807                 //     axis : "0 0 1 1",
33808                 //     axisxlabels :  xdata
33809                 //     //yvalues : cols,
33810                    
33811                 // });
33812 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33813 //        
33814 //        this.load(null,xdata,{
33815 //                axis : "0 0 1 1",
33816 //                axisxlabels :  xdata
33817 //                });
33818
33819     },
33820
33821     load : function(graphtype,xdata,opts)
33822     {
33823         this.raphael.clear();
33824         if(!graphtype) {
33825             graphtype = this.graphtype;
33826         }
33827         if(!opts){
33828             opts = this.opts;
33829         }
33830         var r = this.raphael,
33831             fin = function () {
33832                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33833             },
33834             fout = function () {
33835                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33836             },
33837             pfin = function() {
33838                 this.sector.stop();
33839                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33840
33841                 if (this.label) {
33842                     this.label[0].stop();
33843                     this.label[0].attr({ r: 7.5 });
33844                     this.label[1].attr({ "font-weight": 800 });
33845                 }
33846             },
33847             pfout = function() {
33848                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33849
33850                 if (this.label) {
33851                     this.label[0].animate({ r: 5 }, 500, "bounce");
33852                     this.label[1].attr({ "font-weight": 400 });
33853                 }
33854             };
33855
33856         switch(graphtype){
33857             case 'bar':
33858                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33859                 break;
33860             case 'hbar':
33861                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33862                 break;
33863             case 'pie':
33864 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
33865 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33866 //            
33867                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33868                 
33869                 break;
33870
33871         }
33872         
33873         if(this.title){
33874             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33875         }
33876         
33877     },
33878     
33879     setTitle: function(o)
33880     {
33881         this.title = o;
33882     },
33883     
33884     initEvents: function() {
33885         
33886         if(!this.href){
33887             this.el.on('click', this.onClick, this);
33888         }
33889     },
33890     
33891     onClick : function(e)
33892     {
33893         Roo.log('img onclick');
33894         this.fireEvent('click', this, e);
33895     }
33896    
33897 });
33898
33899  
33900 Roo.bootstrap.dash = {};/*
33901  * - LGPL
33902  *
33903  * numberBox
33904  * 
33905  */
33906 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33907
33908 /**
33909  * @class Roo.bootstrap.dash.NumberBox
33910  * @extends Roo.bootstrap.Component
33911  * Bootstrap NumberBox class
33912  * @cfg {String} headline Box headline
33913  * @cfg {String} content Box content
33914  * @cfg {String} icon Box icon
33915  * @cfg {String} footer Footer text
33916  * @cfg {String} fhref Footer href
33917  * 
33918  * @constructor
33919  * Create a new NumberBox
33920  * @param {Object} config The config object
33921  */
33922
33923
33924 Roo.bootstrap.dash.NumberBox = function(config){
33925     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33926     
33927 };
33928
33929 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
33930     
33931     headline : '',
33932     content : '',
33933     icon : '',
33934     footer : '',
33935     fhref : '',
33936     ficon : '',
33937     
33938     getAutoCreate : function(){
33939         
33940         var cfg = {
33941             tag : 'div',
33942             cls : 'small-box ',
33943             cn : [
33944                 {
33945                     tag : 'div',
33946                     cls : 'inner',
33947                     cn :[
33948                         {
33949                             tag : 'h3',
33950                             cls : 'roo-headline',
33951                             html : this.headline
33952                         },
33953                         {
33954                             tag : 'p',
33955                             cls : 'roo-content',
33956                             html : this.content
33957                         }
33958                     ]
33959                 }
33960             ]
33961         };
33962         
33963         if(this.icon){
33964             cfg.cn.push({
33965                 tag : 'div',
33966                 cls : 'icon',
33967                 cn :[
33968                     {
33969                         tag : 'i',
33970                         cls : 'ion ' + this.icon
33971                     }
33972                 ]
33973             });
33974         }
33975         
33976         if(this.footer){
33977             var footer = {
33978                 tag : 'a',
33979                 cls : 'small-box-footer',
33980                 href : this.fhref || '#',
33981                 html : this.footer
33982             };
33983             
33984             cfg.cn.push(footer);
33985             
33986         }
33987         
33988         return  cfg;
33989     },
33990
33991     onRender : function(ct,position){
33992         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
33993
33994
33995        
33996                 
33997     },
33998
33999     setHeadline: function (value)
34000     {
34001         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34002     },
34003     
34004     setFooter: function (value, href)
34005     {
34006         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34007         
34008         if(href){
34009             this.el.select('a.small-box-footer',true).first().attr('href', href);
34010         }
34011         
34012     },
34013
34014     setContent: function (value)
34015     {
34016         this.el.select('.roo-content',true).first().dom.innerHTML = value;
34017     },
34018
34019     initEvents: function() 
34020     {   
34021         
34022     }
34023     
34024 });
34025
34026  
34027 /*
34028  * - LGPL
34029  *
34030  * TabBox
34031  * 
34032  */
34033 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34034
34035 /**
34036  * @class Roo.bootstrap.dash.TabBox
34037  * @extends Roo.bootstrap.Component
34038  * @children Roo.bootstrap.dash.TabPane
34039  * Bootstrap TabBox class
34040  * @cfg {String} title Title of the TabBox
34041  * @cfg {String} icon Icon of the TabBox
34042  * @cfg {Boolean} showtabs (true|false) show the tabs default true
34043  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34044  * 
34045  * @constructor
34046  * Create a new TabBox
34047  * @param {Object} config The config object
34048  */
34049
34050
34051 Roo.bootstrap.dash.TabBox = function(config){
34052     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34053     this.addEvents({
34054         // raw events
34055         /**
34056          * @event addpane
34057          * When a pane is added
34058          * @param {Roo.bootstrap.dash.TabPane} pane
34059          */
34060         "addpane" : true,
34061         /**
34062          * @event activatepane
34063          * When a pane is activated
34064          * @param {Roo.bootstrap.dash.TabPane} pane
34065          */
34066         "activatepane" : true
34067         
34068          
34069     });
34070     
34071     this.panes = [];
34072 };
34073
34074 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
34075
34076     title : '',
34077     icon : false,
34078     showtabs : true,
34079     tabScrollable : false,
34080     
34081     getChildContainer : function()
34082     {
34083         return this.el.select('.tab-content', true).first();
34084     },
34085     
34086     getAutoCreate : function(){
34087         
34088         var header = {
34089             tag: 'li',
34090             cls: 'pull-left header',
34091             html: this.title,
34092             cn : []
34093         };
34094         
34095         if(this.icon){
34096             header.cn.push({
34097                 tag: 'i',
34098                 cls: 'fa ' + this.icon
34099             });
34100         }
34101         
34102         var h = {
34103             tag: 'ul',
34104             cls: 'nav nav-tabs pull-right',
34105             cn: [
34106                 header
34107             ]
34108         };
34109         
34110         if(this.tabScrollable){
34111             h = {
34112                 tag: 'div',
34113                 cls: 'tab-header',
34114                 cn: [
34115                     {
34116                         tag: 'ul',
34117                         cls: 'nav nav-tabs pull-right',
34118                         cn: [
34119                             header
34120                         ]
34121                     }
34122                 ]
34123             };
34124         }
34125         
34126         var cfg = {
34127             tag: 'div',
34128             cls: 'nav-tabs-custom',
34129             cn: [
34130                 h,
34131                 {
34132                     tag: 'div',
34133                     cls: 'tab-content no-padding',
34134                     cn: []
34135                 }
34136             ]
34137         };
34138
34139         return  cfg;
34140     },
34141     initEvents : function()
34142     {
34143         //Roo.log('add add pane handler');
34144         this.on('addpane', this.onAddPane, this);
34145     },
34146      /**
34147      * Updates the box title
34148      * @param {String} html to set the title to.
34149      */
34150     setTitle : function(value)
34151     {
34152         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34153     },
34154     onAddPane : function(pane)
34155     {
34156         this.panes.push(pane);
34157         //Roo.log('addpane');
34158         //Roo.log(pane);
34159         // tabs are rendere left to right..
34160         if(!this.showtabs){
34161             return;
34162         }
34163         
34164         var ctr = this.el.select('.nav-tabs', true).first();
34165          
34166          
34167         var existing = ctr.select('.nav-tab',true);
34168         var qty = existing.getCount();;
34169         
34170         
34171         var tab = ctr.createChild({
34172             tag : 'li',
34173             cls : 'nav-tab' + (qty ? '' : ' active'),
34174             cn : [
34175                 {
34176                     tag : 'a',
34177                     href:'#',
34178                     html : pane.title
34179                 }
34180             ]
34181         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34182         pane.tab = tab;
34183         
34184         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34185         if (!qty) {
34186             pane.el.addClass('active');
34187         }
34188         
34189                 
34190     },
34191     onTabClick : function(ev,un,ob,pane)
34192     {
34193         //Roo.log('tab - prev default');
34194         ev.preventDefault();
34195         
34196         
34197         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34198         pane.tab.addClass('active');
34199         //Roo.log(pane.title);
34200         this.getChildContainer().select('.tab-pane',true).removeClass('active');
34201         // technically we should have a deactivate event.. but maybe add later.
34202         // and it should not de-activate the selected tab...
34203         this.fireEvent('activatepane', pane);
34204         pane.el.addClass('active');
34205         pane.fireEvent('activate');
34206         
34207         
34208     },
34209     
34210     getActivePane : function()
34211     {
34212         var r = false;
34213         Roo.each(this.panes, function(p) {
34214             if(p.el.hasClass('active')){
34215                 r = p;
34216                 return false;
34217             }
34218             
34219             return;
34220         });
34221         
34222         return r;
34223     }
34224     
34225     
34226 });
34227
34228  
34229 /*
34230  * - LGPL
34231  *
34232  * Tab pane
34233  * 
34234  */
34235 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34236 /**
34237  * @class Roo.bootstrap.TabPane
34238  * @extends Roo.bootstrap.Component
34239  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
34240  * Bootstrap TabPane class
34241  * @cfg {Boolean} active (false | true) Default false
34242  * @cfg {String} title title of panel
34243
34244  * 
34245  * @constructor
34246  * Create a new TabPane
34247  * @param {Object} config The config object
34248  */
34249
34250 Roo.bootstrap.dash.TabPane = function(config){
34251     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34252     
34253     this.addEvents({
34254         // raw events
34255         /**
34256          * @event activate
34257          * When a pane is activated
34258          * @param {Roo.bootstrap.dash.TabPane} pane
34259          */
34260         "activate" : true
34261          
34262     });
34263 };
34264
34265 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
34266     
34267     active : false,
34268     title : '',
34269     
34270     // the tabBox that this is attached to.
34271     tab : false,
34272      
34273     getAutoCreate : function() 
34274     {
34275         var cfg = {
34276             tag: 'div',
34277             cls: 'tab-pane'
34278         };
34279         
34280         if(this.active){
34281             cfg.cls += ' active';
34282         }
34283         
34284         return cfg;
34285     },
34286     initEvents  : function()
34287     {
34288         //Roo.log('trigger add pane handler');
34289         this.parent().fireEvent('addpane', this)
34290     },
34291     
34292      /**
34293      * Updates the tab title 
34294      * @param {String} html to set the title to.
34295      */
34296     setTitle: function(str)
34297     {
34298         if (!this.tab) {
34299             return;
34300         }
34301         this.title = str;
34302         this.tab.select('a', true).first().dom.innerHTML = str;
34303         
34304     }
34305     
34306     
34307     
34308 });
34309
34310  
34311
34312
34313  /*
34314  * - LGPL
34315  *
34316  * Tooltip
34317  * 
34318  */
34319
34320 /**
34321  * @class Roo.bootstrap.Tooltip
34322  * Bootstrap Tooltip class
34323  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34324  * to determine which dom element triggers the tooltip.
34325  * 
34326  * It needs to add support for additional attributes like tooltip-position
34327  * 
34328  * @constructor
34329  * Create a new Toolti
34330  * @param {Object} config The config object
34331  */
34332
34333 Roo.bootstrap.Tooltip = function(config){
34334     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34335     
34336     this.alignment = Roo.bootstrap.Tooltip.alignment;
34337     
34338     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34339         this.alignment = config.alignment;
34340     }
34341     
34342 };
34343
34344 Roo.apply(Roo.bootstrap.Tooltip, {
34345     /**
34346      * @function init initialize tooltip monitoring.
34347      * @static
34348      */
34349     currentEl : false,
34350     currentTip : false,
34351     currentRegion : false,
34352     
34353     //  init : delay?
34354     
34355     init : function()
34356     {
34357         Roo.get(document).on('mouseover', this.enter ,this);
34358         Roo.get(document).on('mouseout', this.leave, this);
34359          
34360         
34361         this.currentTip = new Roo.bootstrap.Tooltip();
34362     },
34363     
34364     enter : function(ev)
34365     {
34366         var dom = ev.getTarget();
34367         
34368         //Roo.log(['enter',dom]);
34369         var el = Roo.fly(dom);
34370         if (this.currentEl) {
34371             //Roo.log(dom);
34372             //Roo.log(this.currentEl);
34373             //Roo.log(this.currentEl.contains(dom));
34374             if (this.currentEl == el) {
34375                 return;
34376             }
34377             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34378                 return;
34379             }
34380
34381         }
34382         
34383         if (this.currentTip.el) {
34384             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34385         }    
34386         //Roo.log(ev);
34387         
34388         if(!el || el.dom == document){
34389             return;
34390         }
34391         
34392         var bindEl = el; 
34393         var pel = false;
34394         if (!el.attr('tooltip')) {
34395             pel = el.findParent("[tooltip]");
34396             if (pel) {
34397                 bindEl = Roo.get(pel);
34398             }
34399         }
34400         
34401        
34402         
34403         // you can not look for children, as if el is the body.. then everythign is the child..
34404         if (!pel && !el.attr('tooltip')) { //
34405             if (!el.select("[tooltip]").elements.length) {
34406                 return;
34407             }
34408             // is the mouse over this child...?
34409             bindEl = el.select("[tooltip]").first();
34410             var xy = ev.getXY();
34411             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34412                 //Roo.log("not in region.");
34413                 return;
34414             }
34415             //Roo.log("child element over..");
34416             
34417         }
34418         this.currentEl = el;
34419         this.currentTip.bind(bindEl);
34420         this.currentRegion = Roo.lib.Region.getRegion(dom);
34421         this.currentTip.enter();
34422         
34423     },
34424     leave : function(ev)
34425     {
34426         var dom = ev.getTarget();
34427         //Roo.log(['leave',dom]);
34428         if (!this.currentEl) {
34429             return;
34430         }
34431         
34432         
34433         if (dom != this.currentEl.dom) {
34434             return;
34435         }
34436         var xy = ev.getXY();
34437         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
34438             return;
34439         }
34440         // only activate leave if mouse cursor is outside... bounding box..
34441         
34442         
34443         
34444         
34445         if (this.currentTip) {
34446             this.currentTip.leave();
34447         }
34448         //Roo.log('clear currentEl');
34449         this.currentEl = false;
34450         
34451         
34452     },
34453     alignment : {
34454         'left' : ['r-l', [-2,0], 'right'],
34455         'right' : ['l-r', [2,0], 'left'],
34456         'bottom' : ['t-b', [0,2], 'top'],
34457         'top' : [ 'b-t', [0,-2], 'bottom']
34458     }
34459     
34460 });
34461
34462
34463 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
34464     
34465     
34466     bindEl : false,
34467     
34468     delay : null, // can be { show : 300 , hide: 500}
34469     
34470     timeout : null,
34471     
34472     hoverState : null, //???
34473     
34474     placement : 'bottom', 
34475     
34476     alignment : false,
34477     
34478     getAutoCreate : function(){
34479     
34480         var cfg = {
34481            cls : 'tooltip',   
34482            role : 'tooltip',
34483            cn : [
34484                 {
34485                     cls : 'tooltip-arrow arrow'
34486                 },
34487                 {
34488                     cls : 'tooltip-inner'
34489                 }
34490            ]
34491         };
34492         
34493         return cfg;
34494     },
34495     bind : function(el)
34496     {
34497         this.bindEl = el;
34498     },
34499     
34500     initEvents : function()
34501     {
34502         this.arrowEl = this.el.select('.arrow', true).first();
34503         this.innerEl = this.el.select('.tooltip-inner', true).first();
34504     },
34505     
34506     enter : function () {
34507        
34508         if (this.timeout != null) {
34509             clearTimeout(this.timeout);
34510         }
34511         
34512         this.hoverState = 'in';
34513          //Roo.log("enter - show");
34514         if (!this.delay || !this.delay.show) {
34515             this.show();
34516             return;
34517         }
34518         var _t = this;
34519         this.timeout = setTimeout(function () {
34520             if (_t.hoverState == 'in') {
34521                 _t.show();
34522             }
34523         }, this.delay.show);
34524     },
34525     leave : function()
34526     {
34527         clearTimeout(this.timeout);
34528     
34529         this.hoverState = 'out';
34530          if (!this.delay || !this.delay.hide) {
34531             this.hide();
34532             return;
34533         }
34534        
34535         var _t = this;
34536         this.timeout = setTimeout(function () {
34537             //Roo.log("leave - timeout");
34538             
34539             if (_t.hoverState == 'out') {
34540                 _t.hide();
34541                 Roo.bootstrap.Tooltip.currentEl = false;
34542             }
34543         }, delay);
34544     },
34545     
34546     show : function (msg)
34547     {
34548         if (!this.el) {
34549             this.render(document.body);
34550         }
34551         // set content.
34552         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34553         
34554         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34555         
34556         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34557         
34558         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34559                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34560
34561         if(this.bindEl.attr('tooltip-class')) {
34562             this.el.addClass(this.bindEl.attr('tooltip-class'));
34563         }
34564         
34565         var placement = typeof this.placement == 'function' ?
34566             this.placement.call(this, this.el, on_el) :
34567             this.placement;
34568         
34569         if(this.bindEl.attr('tooltip-placement')) {
34570             placement = this.bindEl.attr('tooltip-placement');
34571         }
34572             
34573         var autoToken = /\s?auto?\s?/i;
34574         var autoPlace = autoToken.test(placement);
34575         if (autoPlace) {
34576             placement = placement.replace(autoToken, '') || 'top';
34577         }
34578         
34579         //this.el.detach()
34580         //this.el.setXY([0,0]);
34581         this.el.show();
34582         //this.el.dom.style.display='block';
34583         
34584         //this.el.appendTo(on_el);
34585         
34586         var p = this.getPosition();
34587         var box = this.el.getBox();
34588         
34589         if (autoPlace) {
34590             // fixme..
34591         }
34592         
34593         var align = this.alignment[placement];
34594         
34595         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34596         
34597         if(placement == 'top' || placement == 'bottom'){
34598             if(xy[0] < 0){
34599                 placement = 'right';
34600             }
34601             
34602             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34603                 placement = 'left';
34604             }
34605             
34606             var scroll = Roo.select('body', true).first().getScroll();
34607             
34608             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34609                 placement = 'top';
34610             }
34611             
34612             align = this.alignment[placement];
34613             
34614             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34615             
34616         }
34617         
34618         var elems = document.getElementsByTagName('div');
34619         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34620         for (var i = 0; i < elems.length; i++) {
34621           var zindex = Number.parseInt(
34622                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34623                 10
34624           );
34625           if (zindex > highest) {
34626             highest = zindex;
34627           }
34628         }
34629         
34630         
34631         
34632         this.el.dom.style.zIndex = highest;
34633         
34634         this.el.alignTo(this.bindEl, align[0],align[1]);
34635         //var arrow = this.el.select('.arrow',true).first();
34636         //arrow.set(align[2], 
34637         
34638         this.el.addClass(placement);
34639         this.el.addClass("bs-tooltip-"+ placement);
34640         
34641         this.el.addClass('in fade show');
34642         
34643         this.hoverState = null;
34644         
34645         if (this.el.hasClass('fade')) {
34646             // fade it?
34647         }
34648         
34649         
34650         
34651         
34652         
34653     },
34654     hide : function()
34655     {
34656          
34657         if (!this.el) {
34658             return;
34659         }
34660         //this.el.setXY([0,0]);
34661         if(this.bindEl.attr('tooltip-class')) {
34662             this.el.removeClass(this.bindEl.attr('tooltip-class'));
34663         }
34664         this.el.removeClass(['show', 'in']);
34665         //this.el.hide();
34666         
34667     }
34668     
34669 });
34670  
34671
34672  /*
34673  * - LGPL
34674  *
34675  * Location Picker
34676  * 
34677  */
34678
34679 /**
34680  * @class Roo.bootstrap.LocationPicker
34681  * @extends Roo.bootstrap.Component
34682  * Bootstrap LocationPicker class
34683  * @cfg {Number} latitude Position when init default 0
34684  * @cfg {Number} longitude Position when init default 0
34685  * @cfg {Number} zoom default 15
34686  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34687  * @cfg {Boolean} mapTypeControl default false
34688  * @cfg {Boolean} disableDoubleClickZoom default false
34689  * @cfg {Boolean} scrollwheel default true
34690  * @cfg {Boolean} streetViewControl default false
34691  * @cfg {Number} radius default 0
34692  * @cfg {String} locationName
34693  * @cfg {Boolean} draggable default true
34694  * @cfg {Boolean} enableAutocomplete default false
34695  * @cfg {Boolean} enableReverseGeocode default true
34696  * @cfg {String} markerTitle
34697  * 
34698  * @constructor
34699  * Create a new LocationPicker
34700  * @param {Object} config The config object
34701  */
34702
34703
34704 Roo.bootstrap.LocationPicker = function(config){
34705     
34706     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34707     
34708     this.addEvents({
34709         /**
34710          * @event initial
34711          * Fires when the picker initialized.
34712          * @param {Roo.bootstrap.LocationPicker} this
34713          * @param {Google Location} location
34714          */
34715         initial : true,
34716         /**
34717          * @event positionchanged
34718          * Fires when the picker position changed.
34719          * @param {Roo.bootstrap.LocationPicker} this
34720          * @param {Google Location} location
34721          */
34722         positionchanged : true,
34723         /**
34724          * @event resize
34725          * Fires when the map resize.
34726          * @param {Roo.bootstrap.LocationPicker} this
34727          */
34728         resize : true,
34729         /**
34730          * @event show
34731          * Fires when the map show.
34732          * @param {Roo.bootstrap.LocationPicker} this
34733          */
34734         show : true,
34735         /**
34736          * @event hide
34737          * Fires when the map hide.
34738          * @param {Roo.bootstrap.LocationPicker} this
34739          */
34740         hide : true,
34741         /**
34742          * @event mapClick
34743          * Fires when click the map.
34744          * @param {Roo.bootstrap.LocationPicker} this
34745          * @param {Map event} e
34746          */
34747         mapClick : true,
34748         /**
34749          * @event mapRightClick
34750          * Fires when right click the map.
34751          * @param {Roo.bootstrap.LocationPicker} this
34752          * @param {Map event} e
34753          */
34754         mapRightClick : true,
34755         /**
34756          * @event markerClick
34757          * Fires when click the marker.
34758          * @param {Roo.bootstrap.LocationPicker} this
34759          * @param {Map event} e
34760          */
34761         markerClick : true,
34762         /**
34763          * @event markerRightClick
34764          * Fires when right click the marker.
34765          * @param {Roo.bootstrap.LocationPicker} this
34766          * @param {Map event} e
34767          */
34768         markerRightClick : true,
34769         /**
34770          * @event OverlayViewDraw
34771          * Fires when OverlayView Draw
34772          * @param {Roo.bootstrap.LocationPicker} this
34773          */
34774         OverlayViewDraw : true,
34775         /**
34776          * @event OverlayViewOnAdd
34777          * Fires when OverlayView Draw
34778          * @param {Roo.bootstrap.LocationPicker} this
34779          */
34780         OverlayViewOnAdd : true,
34781         /**
34782          * @event OverlayViewOnRemove
34783          * Fires when OverlayView Draw
34784          * @param {Roo.bootstrap.LocationPicker} this
34785          */
34786         OverlayViewOnRemove : true,
34787         /**
34788          * @event OverlayViewShow
34789          * Fires when OverlayView Draw
34790          * @param {Roo.bootstrap.LocationPicker} this
34791          * @param {Pixel} cpx
34792          */
34793         OverlayViewShow : true,
34794         /**
34795          * @event OverlayViewHide
34796          * Fires when OverlayView Draw
34797          * @param {Roo.bootstrap.LocationPicker} this
34798          */
34799         OverlayViewHide : true,
34800         /**
34801          * @event loadexception
34802          * Fires when load google lib failed.
34803          * @param {Roo.bootstrap.LocationPicker} this
34804          */
34805         loadexception : true
34806     });
34807         
34808 };
34809
34810 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
34811     
34812     gMapContext: false,
34813     
34814     latitude: 0,
34815     longitude: 0,
34816     zoom: 15,
34817     mapTypeId: false,
34818     mapTypeControl: false,
34819     disableDoubleClickZoom: false,
34820     scrollwheel: true,
34821     streetViewControl: false,
34822     radius: 0,
34823     locationName: '',
34824     draggable: true,
34825     enableAutocomplete: false,
34826     enableReverseGeocode: true,
34827     markerTitle: '',
34828     
34829     getAutoCreate: function()
34830     {
34831
34832         var cfg = {
34833             tag: 'div',
34834             cls: 'roo-location-picker'
34835         };
34836         
34837         return cfg
34838     },
34839     
34840     initEvents: function(ct, position)
34841     {       
34842         if(!this.el.getWidth() || this.isApplied()){
34843             return;
34844         }
34845         
34846         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34847         
34848         this.initial();
34849     },
34850     
34851     initial: function()
34852     {
34853         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34854             this.fireEvent('loadexception', this);
34855             return;
34856         }
34857         
34858         if(!this.mapTypeId){
34859             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34860         }
34861         
34862         this.gMapContext = this.GMapContext();
34863         
34864         this.initOverlayView();
34865         
34866         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34867         
34868         var _this = this;
34869                 
34870         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34871             _this.setPosition(_this.gMapContext.marker.position);
34872         });
34873         
34874         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34875             _this.fireEvent('mapClick', this, event);
34876             
34877         });
34878
34879         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34880             _this.fireEvent('mapRightClick', this, event);
34881             
34882         });
34883         
34884         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34885             _this.fireEvent('markerClick', this, event);
34886             
34887         });
34888
34889         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34890             _this.fireEvent('markerRightClick', this, event);
34891             
34892         });
34893         
34894         this.setPosition(this.gMapContext.location);
34895         
34896         this.fireEvent('initial', this, this.gMapContext.location);
34897     },
34898     
34899     initOverlayView: function()
34900     {
34901         var _this = this;
34902         
34903         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34904             
34905             draw: function()
34906             {
34907                 _this.fireEvent('OverlayViewDraw', _this);
34908             },
34909             
34910             onAdd: function()
34911             {
34912                 _this.fireEvent('OverlayViewOnAdd', _this);
34913             },
34914             
34915             onRemove: function()
34916             {
34917                 _this.fireEvent('OverlayViewOnRemove', _this);
34918             },
34919             
34920             show: function(cpx)
34921             {
34922                 _this.fireEvent('OverlayViewShow', _this, cpx);
34923             },
34924             
34925             hide: function()
34926             {
34927                 _this.fireEvent('OverlayViewHide', _this);
34928             }
34929             
34930         });
34931     },
34932     
34933     fromLatLngToContainerPixel: function(event)
34934     {
34935         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34936     },
34937     
34938     isApplied: function() 
34939     {
34940         return this.getGmapContext() == false ? false : true;
34941     },
34942     
34943     getGmapContext: function() 
34944     {
34945         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34946     },
34947     
34948     GMapContext: function() 
34949     {
34950         var position = new google.maps.LatLng(this.latitude, this.longitude);
34951         
34952         var _map = new google.maps.Map(this.el.dom, {
34953             center: position,
34954             zoom: this.zoom,
34955             mapTypeId: this.mapTypeId,
34956             mapTypeControl: this.mapTypeControl,
34957             disableDoubleClickZoom: this.disableDoubleClickZoom,
34958             scrollwheel: this.scrollwheel,
34959             streetViewControl: this.streetViewControl,
34960             locationName: this.locationName,
34961             draggable: this.draggable,
34962             enableAutocomplete: this.enableAutocomplete,
34963             enableReverseGeocode: this.enableReverseGeocode
34964         });
34965         
34966         var _marker = new google.maps.Marker({
34967             position: position,
34968             map: _map,
34969             title: this.markerTitle,
34970             draggable: this.draggable
34971         });
34972         
34973         return {
34974             map: _map,
34975             marker: _marker,
34976             circle: null,
34977             location: position,
34978             radius: this.radius,
34979             locationName: this.locationName,
34980             addressComponents: {
34981                 formatted_address: null,
34982                 addressLine1: null,
34983                 addressLine2: null,
34984                 streetName: null,
34985                 streetNumber: null,
34986                 city: null,
34987                 district: null,
34988                 state: null,
34989                 stateOrProvince: null
34990             },
34991             settings: this,
34992             domContainer: this.el.dom,
34993             geodecoder: new google.maps.Geocoder()
34994         };
34995     },
34996     
34997     drawCircle: function(center, radius, options) 
34998     {
34999         if (this.gMapContext.circle != null) {
35000             this.gMapContext.circle.setMap(null);
35001         }
35002         if (radius > 0) {
35003             radius *= 1;
35004             options = Roo.apply({}, options, {
35005                 strokeColor: "#0000FF",
35006                 strokeOpacity: .35,
35007                 strokeWeight: 2,
35008                 fillColor: "#0000FF",
35009                 fillOpacity: .2
35010             });
35011             
35012             options.map = this.gMapContext.map;
35013             options.radius = radius;
35014             options.center = center;
35015             this.gMapContext.circle = new google.maps.Circle(options);
35016             return this.gMapContext.circle;
35017         }
35018         
35019         return null;
35020     },
35021     
35022     setPosition: function(location) 
35023     {
35024         this.gMapContext.location = location;
35025         this.gMapContext.marker.setPosition(location);
35026         this.gMapContext.map.panTo(location);
35027         this.drawCircle(location, this.gMapContext.radius, {});
35028         
35029         var _this = this;
35030         
35031         if (this.gMapContext.settings.enableReverseGeocode) {
35032             this.gMapContext.geodecoder.geocode({
35033                 latLng: this.gMapContext.location
35034             }, function(results, status) {
35035                 
35036                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35037                     _this.gMapContext.locationName = results[0].formatted_address;
35038                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35039                     
35040                     _this.fireEvent('positionchanged', this, location);
35041                 }
35042             });
35043             
35044             return;
35045         }
35046         
35047         this.fireEvent('positionchanged', this, location);
35048     },
35049     
35050     resize: function()
35051     {
35052         google.maps.event.trigger(this.gMapContext.map, "resize");
35053         
35054         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35055         
35056         this.fireEvent('resize', this);
35057     },
35058     
35059     setPositionByLatLng: function(latitude, longitude)
35060     {
35061         this.setPosition(new google.maps.LatLng(latitude, longitude));
35062     },
35063     
35064     getCurrentPosition: function() 
35065     {
35066         return {
35067             latitude: this.gMapContext.location.lat(),
35068             longitude: this.gMapContext.location.lng()
35069         };
35070     },
35071     
35072     getAddressName: function() 
35073     {
35074         return this.gMapContext.locationName;
35075     },
35076     
35077     getAddressComponents: function() 
35078     {
35079         return this.gMapContext.addressComponents;
35080     },
35081     
35082     address_component_from_google_geocode: function(address_components) 
35083     {
35084         var result = {};
35085         
35086         for (var i = 0; i < address_components.length; i++) {
35087             var component = address_components[i];
35088             if (component.types.indexOf("postal_code") >= 0) {
35089                 result.postalCode = component.short_name;
35090             } else if (component.types.indexOf("street_number") >= 0) {
35091                 result.streetNumber = component.short_name;
35092             } else if (component.types.indexOf("route") >= 0) {
35093                 result.streetName = component.short_name;
35094             } else if (component.types.indexOf("neighborhood") >= 0) {
35095                 result.city = component.short_name;
35096             } else if (component.types.indexOf("locality") >= 0) {
35097                 result.city = component.short_name;
35098             } else if (component.types.indexOf("sublocality") >= 0) {
35099                 result.district = component.short_name;
35100             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35101                 result.stateOrProvince = component.short_name;
35102             } else if (component.types.indexOf("country") >= 0) {
35103                 result.country = component.short_name;
35104             }
35105         }
35106         
35107         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35108         result.addressLine2 = "";
35109         return result;
35110     },
35111     
35112     setZoomLevel: function(zoom)
35113     {
35114         this.gMapContext.map.setZoom(zoom);
35115     },
35116     
35117     show: function()
35118     {
35119         if(!this.el){
35120             return;
35121         }
35122         
35123         this.el.show();
35124         
35125         this.resize();
35126         
35127         this.fireEvent('show', this);
35128     },
35129     
35130     hide: function()
35131     {
35132         if(!this.el){
35133             return;
35134         }
35135         
35136         this.el.hide();
35137         
35138         this.fireEvent('hide', this);
35139     }
35140     
35141 });
35142
35143 Roo.apply(Roo.bootstrap.LocationPicker, {
35144     
35145     OverlayView : function(map, options)
35146     {
35147         options = options || {};
35148         
35149         this.setMap(map);
35150     }
35151     
35152     
35153 });/**
35154  * @class Roo.bootstrap.Alert
35155  * @extends Roo.bootstrap.Component
35156  * Bootstrap Alert class - shows an alert area box
35157  * eg
35158  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35159   Enter a valid email address
35160 </div>
35161  * @licence LGPL
35162  * @cfg {String} title The title of alert
35163  * @cfg {String} html The content of alert
35164  * @cfg {String} weight (success|info|warning|danger) Weight of the message
35165  * @cfg {String} fa font-awesomeicon
35166  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35167  * @cfg {Boolean} close true to show a x closer
35168  * 
35169  * 
35170  * @constructor
35171  * Create a new alert
35172  * @param {Object} config The config object
35173  */
35174
35175
35176 Roo.bootstrap.Alert = function(config){
35177     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35178     
35179 };
35180
35181 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
35182     
35183     title: '',
35184     html: '',
35185     weight: false,
35186     fa: false,
35187     faicon: false, // BC
35188     close : false,
35189     
35190     
35191     getAutoCreate : function()
35192     {
35193         
35194         var cfg = {
35195             tag : 'div',
35196             cls : 'alert',
35197             cn : [
35198                 {
35199                     tag: 'button',
35200                     type :  "button",
35201                     cls: "close",
35202                     html : '×',
35203                     style : this.close ? '' : 'display:none'
35204                 },
35205                 {
35206                     tag : 'i',
35207                     cls : 'roo-alert-icon'
35208                     
35209                 },
35210                 {
35211                     tag : 'b',
35212                     cls : 'roo-alert-title',
35213                     html : this.title
35214                 },
35215                 {
35216                     tag : 'span',
35217                     cls : 'roo-alert-text',
35218                     html : this.html
35219                 }
35220             ]
35221         };
35222         
35223         if(this.faicon){
35224             cfg.cn[0].cls += ' fa ' + this.faicon;
35225         }
35226         if(this.fa){
35227             cfg.cn[0].cls += ' fa ' + this.fa;
35228         }
35229         
35230         if(this.weight){
35231             cfg.cls += ' alert-' + this.weight;
35232         }
35233         
35234         return cfg;
35235     },
35236     
35237     initEvents: function() 
35238     {
35239         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35240         this.titleEl =  this.el.select('.roo-alert-title',true).first();
35241         this.iconEl = this.el.select('.roo-alert-icon',true).first();
35242         this.htmlEl = this.el.select('.roo-alert-text',true).first();
35243         if (this.seconds > 0) {
35244             this.hide.defer(this.seconds, this);
35245         }
35246     },
35247     /**
35248      * Set the Title Message HTML
35249      * @param {String} html
35250      */
35251     setTitle : function(str)
35252     {
35253         this.titleEl.dom.innerHTML = str;
35254     },
35255      
35256      /**
35257      * Set the Body Message HTML
35258      * @param {String} html
35259      */
35260     setHtml : function(str)
35261     {
35262         this.htmlEl.dom.innerHTML = str;
35263     },
35264     /**
35265      * Set the Weight of the alert
35266      * @param {String} (success|info|warning|danger) weight
35267      */
35268     
35269     setWeight : function(weight)
35270     {
35271         if(this.weight){
35272             this.el.removeClass('alert-' + this.weight);
35273         }
35274         
35275         this.weight = weight;
35276         
35277         this.el.addClass('alert-' + this.weight);
35278     },
35279       /**
35280      * Set the Icon of the alert
35281      * @param {String} see fontawsome names (name without the 'fa-' bit)
35282      */
35283     setIcon : function(icon)
35284     {
35285         if(this.faicon){
35286             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35287         }
35288         
35289         this.faicon = icon;
35290         
35291         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35292     },
35293     /**
35294      * Hide the Alert
35295      */
35296     hide: function() 
35297     {
35298         this.el.hide();   
35299     },
35300     /**
35301      * Show the Alert
35302      */
35303     show: function() 
35304     {  
35305         this.el.show();   
35306     }
35307     
35308 });
35309
35310  
35311 /*
35312 * Licence: LGPL
35313 */
35314
35315 /**
35316  * @class Roo.bootstrap.UploadCropbox
35317  * @extends Roo.bootstrap.Component
35318  * Bootstrap UploadCropbox class
35319  * @cfg {String} emptyText show when image has been loaded
35320  * @cfg {String} rotateNotify show when image too small to rotate
35321  * @cfg {Number} errorTimeout default 3000
35322  * @cfg {Number} minWidth default 300
35323  * @cfg {Number} minHeight default 300
35324  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35325  * @cfg {Boolean} isDocument (true|false) default false
35326  * @cfg {String} url action url
35327  * @cfg {String} paramName default 'imageUpload'
35328  * @cfg {String} method default POST
35329  * @cfg {Boolean} loadMask (true|false) default true
35330  * @cfg {Boolean} loadingText default 'Loading...'
35331  * 
35332  * @constructor
35333  * Create a new UploadCropbox
35334  * @param {Object} config The config object
35335  */
35336
35337 Roo.bootstrap.UploadCropbox = function(config){
35338     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35339     
35340     this.addEvents({
35341         /**
35342          * @event beforeselectfile
35343          * Fire before select file
35344          * @param {Roo.bootstrap.UploadCropbox} this
35345          */
35346         "beforeselectfile" : true,
35347         /**
35348          * @event initial
35349          * Fire after initEvent
35350          * @param {Roo.bootstrap.UploadCropbox} this
35351          */
35352         "initial" : true,
35353         /**
35354          * @event crop
35355          * Fire after initEvent
35356          * @param {Roo.bootstrap.UploadCropbox} this
35357          * @param {String} data
35358          */
35359         "crop" : true,
35360         /**
35361          * @event prepare
35362          * Fire when preparing the file data
35363          * @param {Roo.bootstrap.UploadCropbox} this
35364          * @param {Object} file
35365          */
35366         "prepare" : true,
35367         /**
35368          * @event exception
35369          * Fire when get exception
35370          * @param {Roo.bootstrap.UploadCropbox} this
35371          * @param {XMLHttpRequest} xhr
35372          */
35373         "exception" : true,
35374         /**
35375          * @event beforeloadcanvas
35376          * Fire before load the canvas
35377          * @param {Roo.bootstrap.UploadCropbox} this
35378          * @param {String} src
35379          */
35380         "beforeloadcanvas" : true,
35381         /**
35382          * @event trash
35383          * Fire when trash image
35384          * @param {Roo.bootstrap.UploadCropbox} this
35385          */
35386         "trash" : true,
35387         /**
35388          * @event download
35389          * Fire when download the image
35390          * @param {Roo.bootstrap.UploadCropbox} this
35391          */
35392         "download" : true,
35393         /**
35394          * @event footerbuttonclick
35395          * Fire when footerbuttonclick
35396          * @param {Roo.bootstrap.UploadCropbox} this
35397          * @param {String} type
35398          */
35399         "footerbuttonclick" : true,
35400         /**
35401          * @event resize
35402          * Fire when resize
35403          * @param {Roo.bootstrap.UploadCropbox} this
35404          */
35405         "resize" : true,
35406         /**
35407          * @event rotate
35408          * Fire when rotate the image
35409          * @param {Roo.bootstrap.UploadCropbox} this
35410          * @param {String} pos
35411          */
35412         "rotate" : true,
35413         /**
35414          * @event inspect
35415          * Fire when inspect the file
35416          * @param {Roo.bootstrap.UploadCropbox} this
35417          * @param {Object} file
35418          */
35419         "inspect" : true,
35420         /**
35421          * @event upload
35422          * Fire when xhr upload the file
35423          * @param {Roo.bootstrap.UploadCropbox} this
35424          * @param {Object} data
35425          */
35426         "upload" : true,
35427         /**
35428          * @event arrange
35429          * Fire when arrange the file data
35430          * @param {Roo.bootstrap.UploadCropbox} this
35431          * @param {Object} formData
35432          */
35433         "arrange" : true
35434     });
35435     
35436     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35437 };
35438
35439 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
35440     
35441     emptyText : 'Click to upload image',
35442     rotateNotify : 'Image is too small to rotate',
35443     errorTimeout : 3000,
35444     scale : 0,
35445     baseScale : 1,
35446     rotate : 0,
35447     dragable : false,
35448     pinching : false,
35449     mouseX : 0,
35450     mouseY : 0,
35451     cropData : false,
35452     minWidth : 300,
35453     minHeight : 300,
35454     file : false,
35455     exif : {},
35456     baseRotate : 1,
35457     cropType : 'image/jpeg',
35458     buttons : false,
35459     canvasLoaded : false,
35460     isDocument : false,
35461     method : 'POST',
35462     paramName : 'imageUpload',
35463     loadMask : true,
35464     loadingText : 'Loading...',
35465     maskEl : false,
35466     
35467     getAutoCreate : function()
35468     {
35469         var cfg = {
35470             tag : 'div',
35471             cls : 'roo-upload-cropbox',
35472             cn : [
35473                 {
35474                     tag : 'input',
35475                     cls : 'roo-upload-cropbox-selector',
35476                     type : 'file'
35477                 },
35478                 {
35479                     tag : 'div',
35480                     cls : 'roo-upload-cropbox-body',
35481                     style : 'cursor:pointer',
35482                     cn : [
35483                         {
35484                             tag : 'div',
35485                             cls : 'roo-upload-cropbox-preview'
35486                         },
35487                         {
35488                             tag : 'div',
35489                             cls : 'roo-upload-cropbox-thumb'
35490                         },
35491                         {
35492                             tag : 'div',
35493                             cls : 'roo-upload-cropbox-empty-notify',
35494                             html : this.emptyText
35495                         },
35496                         {
35497                             tag : 'div',
35498                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35499                             html : this.rotateNotify
35500                         }
35501                     ]
35502                 },
35503                 {
35504                     tag : 'div',
35505                     cls : 'roo-upload-cropbox-footer',
35506                     cn : {
35507                         tag : 'div',
35508                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35509                         cn : []
35510                     }
35511                 }
35512             ]
35513         };
35514         
35515         return cfg;
35516     },
35517     
35518     onRender : function(ct, position)
35519     {
35520         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35521         
35522         if (this.buttons.length) {
35523             
35524             Roo.each(this.buttons, function(bb) {
35525                 
35526                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35527                 
35528                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35529                 
35530             }, this);
35531         }
35532         
35533         if(this.loadMask){
35534             this.maskEl = this.el;
35535         }
35536     },
35537     
35538     initEvents : function()
35539     {
35540         this.urlAPI = (window.createObjectURL && window) || 
35541                                 (window.URL && URL.revokeObjectURL && URL) || 
35542                                 (window.webkitURL && webkitURL);
35543                         
35544         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35545         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35546         
35547         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35548         this.selectorEl.hide();
35549         
35550         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35551         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35552         
35553         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35554         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35555         this.thumbEl.hide();
35556         
35557         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35558         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35559         
35560         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35561         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35562         this.errorEl.hide();
35563         
35564         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35565         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35566         this.footerEl.hide();
35567         
35568         this.setThumbBoxSize();
35569         
35570         this.bind();
35571         
35572         this.resize();
35573         
35574         this.fireEvent('initial', this);
35575     },
35576
35577     bind : function()
35578     {
35579         var _this = this;
35580         
35581         window.addEventListener("resize", function() { _this.resize(); } );
35582         
35583         this.bodyEl.on('click', this.beforeSelectFile, this);
35584         
35585         if(Roo.isTouch){
35586             this.bodyEl.on('touchstart', this.onTouchStart, this);
35587             this.bodyEl.on('touchmove', this.onTouchMove, this);
35588             this.bodyEl.on('touchend', this.onTouchEnd, this);
35589         }
35590         
35591         if(!Roo.isTouch){
35592             this.bodyEl.on('mousedown', this.onMouseDown, this);
35593             this.bodyEl.on('mousemove', this.onMouseMove, this);
35594             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35595             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35596             Roo.get(document).on('mouseup', this.onMouseUp, this);
35597         }
35598         
35599         this.selectorEl.on('change', this.onFileSelected, this);
35600     },
35601     
35602     reset : function()
35603     {    
35604         this.scale = 0;
35605         this.baseScale = 1;
35606         this.rotate = 0;
35607         this.baseRotate = 1;
35608         this.dragable = false;
35609         this.pinching = false;
35610         this.mouseX = 0;
35611         this.mouseY = 0;
35612         this.cropData = false;
35613         this.notifyEl.dom.innerHTML = this.emptyText;
35614         
35615         this.selectorEl.dom.value = '';
35616         
35617     },
35618     
35619     resize : function()
35620     {
35621         if(this.fireEvent('resize', this) != false){
35622             this.setThumbBoxPosition();
35623             this.setCanvasPosition();
35624         }
35625     },
35626     
35627     onFooterButtonClick : function(e, el, o, type)
35628     {
35629         switch (type) {
35630             case 'rotate-left' :
35631                 this.onRotateLeft(e);
35632                 break;
35633             case 'rotate-right' :
35634                 this.onRotateRight(e);
35635                 break;
35636             case 'picture' :
35637                 this.beforeSelectFile(e);
35638                 break;
35639             case 'trash' :
35640                 this.trash(e);
35641                 break;
35642             case 'crop' :
35643                 this.crop(e);
35644                 break;
35645             case 'download' :
35646                 this.download(e);
35647                 break;
35648             default :
35649                 break;
35650         }
35651         
35652         this.fireEvent('footerbuttonclick', this, type);
35653     },
35654     
35655     beforeSelectFile : function(e)
35656     {
35657         e.preventDefault();
35658         
35659         if(this.fireEvent('beforeselectfile', this) != false){
35660             this.selectorEl.dom.click();
35661         }
35662     },
35663     
35664     onFileSelected : function(e)
35665     {
35666         e.preventDefault();
35667         
35668         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35669             return;
35670         }
35671         
35672         var file = this.selectorEl.dom.files[0];
35673         
35674         if(this.fireEvent('inspect', this, file) != false){
35675             this.prepare(file);
35676         }
35677         
35678     },
35679     
35680     trash : function(e)
35681     {
35682         this.fireEvent('trash', this);
35683     },
35684     
35685     download : function(e)
35686     {
35687         this.fireEvent('download', this);
35688     },
35689     
35690     loadCanvas : function(src)
35691     {   
35692         if(this.fireEvent('beforeloadcanvas', this, src) != false){
35693             
35694             this.reset();
35695             
35696             this.imageEl = document.createElement('img');
35697             
35698             var _this = this;
35699             
35700             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35701             
35702             this.imageEl.src = src;
35703         }
35704     },
35705     
35706     onLoadCanvas : function()
35707     {   
35708         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35709         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35710         
35711         this.bodyEl.un('click', this.beforeSelectFile, this);
35712         
35713         this.notifyEl.hide();
35714         this.thumbEl.show();
35715         this.footerEl.show();
35716         
35717         this.baseRotateLevel();
35718         
35719         if(this.isDocument){
35720             this.setThumbBoxSize();
35721         }
35722         
35723         this.setThumbBoxPosition();
35724         
35725         this.baseScaleLevel();
35726         
35727         this.draw();
35728         
35729         this.resize();
35730         
35731         this.canvasLoaded = true;
35732         
35733         if(this.loadMask){
35734             this.maskEl.unmask();
35735         }
35736         
35737     },
35738     
35739     setCanvasPosition : function()
35740     {   
35741         if(!this.canvasEl){
35742             return;
35743         }
35744         
35745         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35746         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35747         
35748         this.previewEl.setLeft(pw);
35749         this.previewEl.setTop(ph);
35750         
35751     },
35752     
35753     onMouseDown : function(e)
35754     {   
35755         e.stopEvent();
35756         
35757         this.dragable = true;
35758         this.pinching = false;
35759         
35760         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35761             this.dragable = false;
35762             return;
35763         }
35764         
35765         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35766         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35767         
35768     },
35769     
35770     onMouseMove : function(e)
35771     {   
35772         e.stopEvent();
35773         
35774         if(!this.canvasLoaded){
35775             return;
35776         }
35777         
35778         if (!this.dragable){
35779             return;
35780         }
35781         
35782         var minX = Math.ceil(this.thumbEl.getLeft(true));
35783         var minY = Math.ceil(this.thumbEl.getTop(true));
35784         
35785         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35786         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35787         
35788         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35789         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35790         
35791         x = x - this.mouseX;
35792         y = y - this.mouseY;
35793         
35794         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35795         var bgY = Math.ceil(y + this.previewEl.getTop(true));
35796         
35797         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35798         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35799         
35800         this.previewEl.setLeft(bgX);
35801         this.previewEl.setTop(bgY);
35802         
35803         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35804         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35805     },
35806     
35807     onMouseUp : function(e)
35808     {   
35809         e.stopEvent();
35810         
35811         this.dragable = false;
35812     },
35813     
35814     onMouseWheel : function(e)
35815     {   
35816         e.stopEvent();
35817         
35818         this.startScale = this.scale;
35819         
35820         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35821         
35822         if(!this.zoomable()){
35823             this.scale = this.startScale;
35824             return;
35825         }
35826         
35827         this.draw();
35828         
35829         return;
35830     },
35831     
35832     zoomable : function()
35833     {
35834         var minScale = this.thumbEl.getWidth() / this.minWidth;
35835         
35836         if(this.minWidth < this.minHeight){
35837             minScale = this.thumbEl.getHeight() / this.minHeight;
35838         }
35839         
35840         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35841         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35842         
35843         if(
35844                 this.isDocument &&
35845                 (this.rotate == 0 || this.rotate == 180) && 
35846                 (
35847                     width > this.imageEl.OriginWidth || 
35848                     height > this.imageEl.OriginHeight ||
35849                     (width < this.minWidth && height < this.minHeight)
35850                 )
35851         ){
35852             return false;
35853         }
35854         
35855         if(
35856                 this.isDocument &&
35857                 (this.rotate == 90 || this.rotate == 270) && 
35858                 (
35859                     width > this.imageEl.OriginWidth || 
35860                     height > this.imageEl.OriginHeight ||
35861                     (width < this.minHeight && height < this.minWidth)
35862                 )
35863         ){
35864             return false;
35865         }
35866         
35867         if(
35868                 !this.isDocument &&
35869                 (this.rotate == 0 || this.rotate == 180) && 
35870                 (
35871                     width < this.minWidth || 
35872                     width > this.imageEl.OriginWidth || 
35873                     height < this.minHeight || 
35874                     height > this.imageEl.OriginHeight
35875                 )
35876         ){
35877             return false;
35878         }
35879         
35880         if(
35881                 !this.isDocument &&
35882                 (this.rotate == 90 || this.rotate == 270) && 
35883                 (
35884                     width < this.minHeight || 
35885                     width > this.imageEl.OriginWidth || 
35886                     height < this.minWidth || 
35887                     height > this.imageEl.OriginHeight
35888                 )
35889         ){
35890             return false;
35891         }
35892         
35893         return true;
35894         
35895     },
35896     
35897     onRotateLeft : function(e)
35898     {   
35899         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35900             
35901             var minScale = this.thumbEl.getWidth() / this.minWidth;
35902             
35903             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35904             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35905             
35906             this.startScale = this.scale;
35907             
35908             while (this.getScaleLevel() < minScale){
35909             
35910                 this.scale = this.scale + 1;
35911                 
35912                 if(!this.zoomable()){
35913                     break;
35914                 }
35915                 
35916                 if(
35917                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35918                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35919                 ){
35920                     continue;
35921                 }
35922                 
35923                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35924
35925                 this.draw();
35926                 
35927                 return;
35928             }
35929             
35930             this.scale = this.startScale;
35931             
35932             this.onRotateFail();
35933             
35934             return false;
35935         }
35936         
35937         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35938
35939         if(this.isDocument){
35940             this.setThumbBoxSize();
35941             this.setThumbBoxPosition();
35942             this.setCanvasPosition();
35943         }
35944         
35945         this.draw();
35946         
35947         this.fireEvent('rotate', this, 'left');
35948         
35949     },
35950     
35951     onRotateRight : function(e)
35952     {
35953         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35954             
35955             var minScale = this.thumbEl.getWidth() / this.minWidth;
35956         
35957             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35958             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35959             
35960             this.startScale = this.scale;
35961             
35962             while (this.getScaleLevel() < minScale){
35963             
35964                 this.scale = this.scale + 1;
35965                 
35966                 if(!this.zoomable()){
35967                     break;
35968                 }
35969                 
35970                 if(
35971                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35972                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35973                 ){
35974                     continue;
35975                 }
35976                 
35977                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35978
35979                 this.draw();
35980                 
35981                 return;
35982             }
35983             
35984             this.scale = this.startScale;
35985             
35986             this.onRotateFail();
35987             
35988             return false;
35989         }
35990         
35991         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35992
35993         if(this.isDocument){
35994             this.setThumbBoxSize();
35995             this.setThumbBoxPosition();
35996             this.setCanvasPosition();
35997         }
35998         
35999         this.draw();
36000         
36001         this.fireEvent('rotate', this, 'right');
36002     },
36003     
36004     onRotateFail : function()
36005     {
36006         this.errorEl.show(true);
36007         
36008         var _this = this;
36009         
36010         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36011     },
36012     
36013     draw : function()
36014     {
36015         this.previewEl.dom.innerHTML = '';
36016         
36017         var canvasEl = document.createElement("canvas");
36018         
36019         var contextEl = canvasEl.getContext("2d");
36020         
36021         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36022         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36023         var center = this.imageEl.OriginWidth / 2;
36024         
36025         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36026             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36027             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36028             center = this.imageEl.OriginHeight / 2;
36029         }
36030         
36031         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36032         
36033         contextEl.translate(center, center);
36034         contextEl.rotate(this.rotate * Math.PI / 180);
36035
36036         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36037         
36038         this.canvasEl = document.createElement("canvas");
36039         
36040         this.contextEl = this.canvasEl.getContext("2d");
36041         
36042         switch (this.rotate) {
36043             case 0 :
36044                 
36045                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36046                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36047                 
36048                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36049                 
36050                 break;
36051             case 90 : 
36052                 
36053                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36054                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36055                 
36056                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36057                     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);
36058                     break;
36059                 }
36060                 
36061                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36062                 
36063                 break;
36064             case 180 :
36065                 
36066                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36067                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36068                 
36069                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36070                     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);
36071                     break;
36072                 }
36073                 
36074                 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);
36075                 
36076                 break;
36077             case 270 :
36078                 
36079                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36080                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36081         
36082                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36083                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36084                     break;
36085                 }
36086                 
36087                 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);
36088                 
36089                 break;
36090             default : 
36091                 break;
36092         }
36093         
36094         this.previewEl.appendChild(this.canvasEl);
36095         
36096         this.setCanvasPosition();
36097     },
36098     
36099     crop : function()
36100     {
36101         if(!this.canvasLoaded){
36102             return;
36103         }
36104         
36105         var imageCanvas = document.createElement("canvas");
36106         
36107         var imageContext = imageCanvas.getContext("2d");
36108         
36109         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36110         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36111         
36112         var center = imageCanvas.width / 2;
36113         
36114         imageContext.translate(center, center);
36115         
36116         imageContext.rotate(this.rotate * Math.PI / 180);
36117         
36118         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36119         
36120         var canvas = document.createElement("canvas");
36121         
36122         var context = canvas.getContext("2d");
36123                 
36124         canvas.width = this.minWidth;
36125         canvas.height = this.minHeight;
36126
36127         switch (this.rotate) {
36128             case 0 :
36129                 
36130                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36131                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36132                 
36133                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36134                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36135                 
36136                 var targetWidth = this.minWidth - 2 * x;
36137                 var targetHeight = this.minHeight - 2 * y;
36138                 
36139                 var scale = 1;
36140                 
36141                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36142                     scale = targetWidth / width;
36143                 }
36144                 
36145                 if(x > 0 && y == 0){
36146                     scale = targetHeight / height;
36147                 }
36148                 
36149                 if(x > 0 && y > 0){
36150                     scale = targetWidth / width;
36151                     
36152                     if(width < height){
36153                         scale = targetHeight / height;
36154                     }
36155                 }
36156                 
36157                 context.scale(scale, scale);
36158                 
36159                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36160                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36161
36162                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36163                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36164
36165                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36166                 
36167                 break;
36168             case 90 : 
36169                 
36170                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36171                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36172                 
36173                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36174                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36175                 
36176                 var targetWidth = this.minWidth - 2 * x;
36177                 var targetHeight = this.minHeight - 2 * y;
36178                 
36179                 var scale = 1;
36180                 
36181                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36182                     scale = targetWidth / width;
36183                 }
36184                 
36185                 if(x > 0 && y == 0){
36186                     scale = targetHeight / height;
36187                 }
36188                 
36189                 if(x > 0 && y > 0){
36190                     scale = targetWidth / width;
36191                     
36192                     if(width < height){
36193                         scale = targetHeight / height;
36194                     }
36195                 }
36196                 
36197                 context.scale(scale, scale);
36198                 
36199                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36200                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36201
36202                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36203                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36204                 
36205                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36206                 
36207                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36208                 
36209                 break;
36210             case 180 :
36211                 
36212                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36213                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36214                 
36215                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36216                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36217                 
36218                 var targetWidth = this.minWidth - 2 * x;
36219                 var targetHeight = this.minHeight - 2 * y;
36220                 
36221                 var scale = 1;
36222                 
36223                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36224                     scale = targetWidth / width;
36225                 }
36226                 
36227                 if(x > 0 && y == 0){
36228                     scale = targetHeight / height;
36229                 }
36230                 
36231                 if(x > 0 && y > 0){
36232                     scale = targetWidth / width;
36233                     
36234                     if(width < height){
36235                         scale = targetHeight / height;
36236                     }
36237                 }
36238                 
36239                 context.scale(scale, scale);
36240                 
36241                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36242                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36243
36244                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36245                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36246
36247                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36248                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36249                 
36250                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36251                 
36252                 break;
36253             case 270 :
36254                 
36255                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36256                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36257                 
36258                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36259                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36260                 
36261                 var targetWidth = this.minWidth - 2 * x;
36262                 var targetHeight = this.minHeight - 2 * y;
36263                 
36264                 var scale = 1;
36265                 
36266                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36267                     scale = targetWidth / width;
36268                 }
36269                 
36270                 if(x > 0 && y == 0){
36271                     scale = targetHeight / height;
36272                 }
36273                 
36274                 if(x > 0 && y > 0){
36275                     scale = targetWidth / width;
36276                     
36277                     if(width < height){
36278                         scale = targetHeight / height;
36279                     }
36280                 }
36281                 
36282                 context.scale(scale, scale);
36283                 
36284                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36285                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36286
36287                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36288                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36289                 
36290                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36291                 
36292                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36293                 
36294                 break;
36295             default : 
36296                 break;
36297         }
36298         
36299         this.cropData = canvas.toDataURL(this.cropType);
36300         
36301         if(this.fireEvent('crop', this, this.cropData) !== false){
36302             this.process(this.file, this.cropData);
36303         }
36304         
36305         return;
36306         
36307     },
36308     
36309     setThumbBoxSize : function()
36310     {
36311         var width, height;
36312         
36313         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36314             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36315             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36316             
36317             this.minWidth = width;
36318             this.minHeight = height;
36319             
36320             if(this.rotate == 90 || this.rotate == 270){
36321                 this.minWidth = height;
36322                 this.minHeight = width;
36323             }
36324         }
36325         
36326         height = 300;
36327         width = Math.ceil(this.minWidth * height / this.minHeight);
36328         
36329         if(this.minWidth > this.minHeight){
36330             width = 300;
36331             height = Math.ceil(this.minHeight * width / this.minWidth);
36332         }
36333         
36334         this.thumbEl.setStyle({
36335             width : width + 'px',
36336             height : height + 'px'
36337         });
36338
36339         return;
36340             
36341     },
36342     
36343     setThumbBoxPosition : function()
36344     {
36345         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36346         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36347         
36348         this.thumbEl.setLeft(x);
36349         this.thumbEl.setTop(y);
36350         
36351     },
36352     
36353     baseRotateLevel : function()
36354     {
36355         this.baseRotate = 1;
36356         
36357         if(
36358                 typeof(this.exif) != 'undefined' &&
36359                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36360                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36361         ){
36362             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36363         }
36364         
36365         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36366         
36367     },
36368     
36369     baseScaleLevel : function()
36370     {
36371         var width, height;
36372         
36373         if(this.isDocument){
36374             
36375             if(this.baseRotate == 6 || this.baseRotate == 8){
36376             
36377                 height = this.thumbEl.getHeight();
36378                 this.baseScale = height / this.imageEl.OriginWidth;
36379
36380                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36381                     width = this.thumbEl.getWidth();
36382                     this.baseScale = width / this.imageEl.OriginHeight;
36383                 }
36384
36385                 return;
36386             }
36387
36388             height = this.thumbEl.getHeight();
36389             this.baseScale = height / this.imageEl.OriginHeight;
36390
36391             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36392                 width = this.thumbEl.getWidth();
36393                 this.baseScale = width / this.imageEl.OriginWidth;
36394             }
36395
36396             return;
36397         }
36398         
36399         if(this.baseRotate == 6 || this.baseRotate == 8){
36400             
36401             width = this.thumbEl.getHeight();
36402             this.baseScale = width / this.imageEl.OriginHeight;
36403             
36404             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36405                 height = this.thumbEl.getWidth();
36406                 this.baseScale = height / this.imageEl.OriginHeight;
36407             }
36408             
36409             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36410                 height = this.thumbEl.getWidth();
36411                 this.baseScale = height / this.imageEl.OriginHeight;
36412                 
36413                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36414                     width = this.thumbEl.getHeight();
36415                     this.baseScale = width / this.imageEl.OriginWidth;
36416                 }
36417             }
36418             
36419             return;
36420         }
36421         
36422         width = this.thumbEl.getWidth();
36423         this.baseScale = width / this.imageEl.OriginWidth;
36424         
36425         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36426             height = this.thumbEl.getHeight();
36427             this.baseScale = height / this.imageEl.OriginHeight;
36428         }
36429         
36430         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36431             
36432             height = this.thumbEl.getHeight();
36433             this.baseScale = height / this.imageEl.OriginHeight;
36434             
36435             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36436                 width = this.thumbEl.getWidth();
36437                 this.baseScale = width / this.imageEl.OriginWidth;
36438             }
36439             
36440         }
36441         
36442         return;
36443     },
36444     
36445     getScaleLevel : function()
36446     {
36447         return this.baseScale * Math.pow(1.1, this.scale);
36448     },
36449     
36450     onTouchStart : function(e)
36451     {
36452         if(!this.canvasLoaded){
36453             this.beforeSelectFile(e);
36454             return;
36455         }
36456         
36457         var touches = e.browserEvent.touches;
36458         
36459         if(!touches){
36460             return;
36461         }
36462         
36463         if(touches.length == 1){
36464             this.onMouseDown(e);
36465             return;
36466         }
36467         
36468         if(touches.length != 2){
36469             return;
36470         }
36471         
36472         var coords = [];
36473         
36474         for(var i = 0, finger; finger = touches[i]; i++){
36475             coords.push(finger.pageX, finger.pageY);
36476         }
36477         
36478         var x = Math.pow(coords[0] - coords[2], 2);
36479         var y = Math.pow(coords[1] - coords[3], 2);
36480         
36481         this.startDistance = Math.sqrt(x + y);
36482         
36483         this.startScale = this.scale;
36484         
36485         this.pinching = true;
36486         this.dragable = false;
36487         
36488     },
36489     
36490     onTouchMove : function(e)
36491     {
36492         if(!this.pinching && !this.dragable){
36493             return;
36494         }
36495         
36496         var touches = e.browserEvent.touches;
36497         
36498         if(!touches){
36499             return;
36500         }
36501         
36502         if(this.dragable){
36503             this.onMouseMove(e);
36504             return;
36505         }
36506         
36507         var coords = [];
36508         
36509         for(var i = 0, finger; finger = touches[i]; i++){
36510             coords.push(finger.pageX, finger.pageY);
36511         }
36512         
36513         var x = Math.pow(coords[0] - coords[2], 2);
36514         var y = Math.pow(coords[1] - coords[3], 2);
36515         
36516         this.endDistance = Math.sqrt(x + y);
36517         
36518         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36519         
36520         if(!this.zoomable()){
36521             this.scale = this.startScale;
36522             return;
36523         }
36524         
36525         this.draw();
36526         
36527     },
36528     
36529     onTouchEnd : function(e)
36530     {
36531         this.pinching = false;
36532         this.dragable = false;
36533         
36534     },
36535     
36536     process : function(file, crop)
36537     {
36538         if(this.loadMask){
36539             this.maskEl.mask(this.loadingText);
36540         }
36541         
36542         this.xhr = new XMLHttpRequest();
36543         
36544         file.xhr = this.xhr;
36545
36546         this.xhr.open(this.method, this.url, true);
36547         
36548         var headers = {
36549             "Accept": "application/json",
36550             "Cache-Control": "no-cache",
36551             "X-Requested-With": "XMLHttpRequest"
36552         };
36553         
36554         for (var headerName in headers) {
36555             var headerValue = headers[headerName];
36556             if (headerValue) {
36557                 this.xhr.setRequestHeader(headerName, headerValue);
36558             }
36559         }
36560         
36561         var _this = this;
36562         
36563         this.xhr.onload = function()
36564         {
36565             _this.xhrOnLoad(_this.xhr);
36566         }
36567         
36568         this.xhr.onerror = function()
36569         {
36570             _this.xhrOnError(_this.xhr);
36571         }
36572         
36573         var formData = new FormData();
36574
36575         formData.append('returnHTML', 'NO');
36576         
36577         if(crop){
36578             formData.append('crop', crop);
36579         }
36580         
36581         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36582             formData.append(this.paramName, file, file.name);
36583         }
36584         
36585         if(typeof(file.filename) != 'undefined'){
36586             formData.append('filename', file.filename);
36587         }
36588         
36589         if(typeof(file.mimetype) != 'undefined'){
36590             formData.append('mimetype', file.mimetype);
36591         }
36592         
36593         if(this.fireEvent('arrange', this, formData) != false){
36594             this.xhr.send(formData);
36595         };
36596     },
36597     
36598     xhrOnLoad : function(xhr)
36599     {
36600         if(this.loadMask){
36601             this.maskEl.unmask();
36602         }
36603         
36604         if (xhr.readyState !== 4) {
36605             this.fireEvent('exception', this, xhr);
36606             return;
36607         }
36608
36609         var response = Roo.decode(xhr.responseText);
36610         
36611         if(!response.success){
36612             this.fireEvent('exception', this, xhr);
36613             return;
36614         }
36615         
36616         var response = Roo.decode(xhr.responseText);
36617         
36618         this.fireEvent('upload', this, response);
36619         
36620     },
36621     
36622     xhrOnError : function()
36623     {
36624         if(this.loadMask){
36625             this.maskEl.unmask();
36626         }
36627         
36628         Roo.log('xhr on error');
36629         
36630         var response = Roo.decode(xhr.responseText);
36631           
36632         Roo.log(response);
36633         
36634     },
36635     
36636     prepare : function(file)
36637     {   
36638         if(this.loadMask){
36639             this.maskEl.mask(this.loadingText);
36640         }
36641         
36642         this.file = false;
36643         this.exif = {};
36644         
36645         if(typeof(file) === 'string'){
36646             this.loadCanvas(file);
36647             return;
36648         }
36649         
36650         if(!file || !this.urlAPI){
36651             return;
36652         }
36653         
36654         this.file = file;
36655         this.cropType = file.type;
36656         
36657         var _this = this;
36658         
36659         if(this.fireEvent('prepare', this, this.file) != false){
36660             
36661             var reader = new FileReader();
36662             
36663             reader.onload = function (e) {
36664                 if (e.target.error) {
36665                     Roo.log(e.target.error);
36666                     return;
36667                 }
36668                 
36669                 var buffer = e.target.result,
36670                     dataView = new DataView(buffer),
36671                     offset = 2,
36672                     maxOffset = dataView.byteLength - 4,
36673                     markerBytes,
36674                     markerLength;
36675                 
36676                 if (dataView.getUint16(0) === 0xffd8) {
36677                     while (offset < maxOffset) {
36678                         markerBytes = dataView.getUint16(offset);
36679                         
36680                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36681                             markerLength = dataView.getUint16(offset + 2) + 2;
36682                             if (offset + markerLength > dataView.byteLength) {
36683                                 Roo.log('Invalid meta data: Invalid segment size.');
36684                                 break;
36685                             }
36686                             
36687                             if(markerBytes == 0xffe1){
36688                                 _this.parseExifData(
36689                                     dataView,
36690                                     offset,
36691                                     markerLength
36692                                 );
36693                             }
36694                             
36695                             offset += markerLength;
36696                             
36697                             continue;
36698                         }
36699                         
36700                         break;
36701                     }
36702                     
36703                 }
36704                 
36705                 var url = _this.urlAPI.createObjectURL(_this.file);
36706                 
36707                 _this.loadCanvas(url);
36708                 
36709                 return;
36710             }
36711             
36712             reader.readAsArrayBuffer(this.file);
36713             
36714         }
36715         
36716     },
36717     
36718     parseExifData : function(dataView, offset, length)
36719     {
36720         var tiffOffset = offset + 10,
36721             littleEndian,
36722             dirOffset;
36723     
36724         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36725             // No Exif data, might be XMP data instead
36726             return;
36727         }
36728         
36729         // Check for the ASCII code for "Exif" (0x45786966):
36730         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36731             // No Exif data, might be XMP data instead
36732             return;
36733         }
36734         if (tiffOffset + 8 > dataView.byteLength) {
36735             Roo.log('Invalid Exif data: Invalid segment size.');
36736             return;
36737         }
36738         // Check for the two null bytes:
36739         if (dataView.getUint16(offset + 8) !== 0x0000) {
36740             Roo.log('Invalid Exif data: Missing byte alignment offset.');
36741             return;
36742         }
36743         // Check the byte alignment:
36744         switch (dataView.getUint16(tiffOffset)) {
36745         case 0x4949:
36746             littleEndian = true;
36747             break;
36748         case 0x4D4D:
36749             littleEndian = false;
36750             break;
36751         default:
36752             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36753             return;
36754         }
36755         // Check for the TIFF tag marker (0x002A):
36756         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36757             Roo.log('Invalid Exif data: Missing TIFF marker.');
36758             return;
36759         }
36760         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36761         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36762         
36763         this.parseExifTags(
36764             dataView,
36765             tiffOffset,
36766             tiffOffset + dirOffset,
36767             littleEndian
36768         );
36769     },
36770     
36771     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36772     {
36773         var tagsNumber,
36774             dirEndOffset,
36775             i;
36776         if (dirOffset + 6 > dataView.byteLength) {
36777             Roo.log('Invalid Exif data: Invalid directory offset.');
36778             return;
36779         }
36780         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36781         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36782         if (dirEndOffset + 4 > dataView.byteLength) {
36783             Roo.log('Invalid Exif data: Invalid directory size.');
36784             return;
36785         }
36786         for (i = 0; i < tagsNumber; i += 1) {
36787             this.parseExifTag(
36788                 dataView,
36789                 tiffOffset,
36790                 dirOffset + 2 + 12 * i, // tag offset
36791                 littleEndian
36792             );
36793         }
36794         // Return the offset to the next directory:
36795         return dataView.getUint32(dirEndOffset, littleEndian);
36796     },
36797     
36798     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
36799     {
36800         var tag = dataView.getUint16(offset, littleEndian);
36801         
36802         this.exif[tag] = this.getExifValue(
36803             dataView,
36804             tiffOffset,
36805             offset,
36806             dataView.getUint16(offset + 2, littleEndian), // tag type
36807             dataView.getUint32(offset + 4, littleEndian), // tag length
36808             littleEndian
36809         );
36810     },
36811     
36812     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36813     {
36814         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36815             tagSize,
36816             dataOffset,
36817             values,
36818             i,
36819             str,
36820             c;
36821     
36822         if (!tagType) {
36823             Roo.log('Invalid Exif data: Invalid tag type.');
36824             return;
36825         }
36826         
36827         tagSize = tagType.size * length;
36828         // Determine if the value is contained in the dataOffset bytes,
36829         // or if the value at the dataOffset is a pointer to the actual data:
36830         dataOffset = tagSize > 4 ?
36831                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36832         if (dataOffset + tagSize > dataView.byteLength) {
36833             Roo.log('Invalid Exif data: Invalid data offset.');
36834             return;
36835         }
36836         if (length === 1) {
36837             return tagType.getValue(dataView, dataOffset, littleEndian);
36838         }
36839         values = [];
36840         for (i = 0; i < length; i += 1) {
36841             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36842         }
36843         
36844         if (tagType.ascii) {
36845             str = '';
36846             // Concatenate the chars:
36847             for (i = 0; i < values.length; i += 1) {
36848                 c = values[i];
36849                 // Ignore the terminating NULL byte(s):
36850                 if (c === '\u0000') {
36851                     break;
36852                 }
36853                 str += c;
36854             }
36855             return str;
36856         }
36857         return values;
36858     }
36859     
36860 });
36861
36862 Roo.apply(Roo.bootstrap.UploadCropbox, {
36863     tags : {
36864         'Orientation': 0x0112
36865     },
36866     
36867     Orientation: {
36868             1: 0, //'top-left',
36869 //            2: 'top-right',
36870             3: 180, //'bottom-right',
36871 //            4: 'bottom-left',
36872 //            5: 'left-top',
36873             6: 90, //'right-top',
36874 //            7: 'right-bottom',
36875             8: 270 //'left-bottom'
36876     },
36877     
36878     exifTagTypes : {
36879         // byte, 8-bit unsigned int:
36880         1: {
36881             getValue: function (dataView, dataOffset) {
36882                 return dataView.getUint8(dataOffset);
36883             },
36884             size: 1
36885         },
36886         // ascii, 8-bit byte:
36887         2: {
36888             getValue: function (dataView, dataOffset) {
36889                 return String.fromCharCode(dataView.getUint8(dataOffset));
36890             },
36891             size: 1,
36892             ascii: true
36893         },
36894         // short, 16 bit int:
36895         3: {
36896             getValue: function (dataView, dataOffset, littleEndian) {
36897                 return dataView.getUint16(dataOffset, littleEndian);
36898             },
36899             size: 2
36900         },
36901         // long, 32 bit int:
36902         4: {
36903             getValue: function (dataView, dataOffset, littleEndian) {
36904                 return dataView.getUint32(dataOffset, littleEndian);
36905             },
36906             size: 4
36907         },
36908         // rational = two long values, first is numerator, second is denominator:
36909         5: {
36910             getValue: function (dataView, dataOffset, littleEndian) {
36911                 return dataView.getUint32(dataOffset, littleEndian) /
36912                     dataView.getUint32(dataOffset + 4, littleEndian);
36913             },
36914             size: 8
36915         },
36916         // slong, 32 bit signed int:
36917         9: {
36918             getValue: function (dataView, dataOffset, littleEndian) {
36919                 return dataView.getInt32(dataOffset, littleEndian);
36920             },
36921             size: 4
36922         },
36923         // srational, two slongs, first is numerator, second is denominator:
36924         10: {
36925             getValue: function (dataView, dataOffset, littleEndian) {
36926                 return dataView.getInt32(dataOffset, littleEndian) /
36927                     dataView.getInt32(dataOffset + 4, littleEndian);
36928             },
36929             size: 8
36930         }
36931     },
36932     
36933     footer : {
36934         STANDARD : [
36935             {
36936                 tag : 'div',
36937                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36938                 action : 'rotate-left',
36939                 cn : [
36940                     {
36941                         tag : 'button',
36942                         cls : 'btn btn-default',
36943                         html : '<i class="fa fa-undo"></i>'
36944                     }
36945                 ]
36946             },
36947             {
36948                 tag : 'div',
36949                 cls : 'btn-group roo-upload-cropbox-picture',
36950                 action : 'picture',
36951                 cn : [
36952                     {
36953                         tag : 'button',
36954                         cls : 'btn btn-default',
36955                         html : '<i class="fa fa-picture-o"></i>'
36956                     }
36957                 ]
36958             },
36959             {
36960                 tag : 'div',
36961                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36962                 action : 'rotate-right',
36963                 cn : [
36964                     {
36965                         tag : 'button',
36966                         cls : 'btn btn-default',
36967                         html : '<i class="fa fa-repeat"></i>'
36968                     }
36969                 ]
36970             }
36971         ],
36972         DOCUMENT : [
36973             {
36974                 tag : 'div',
36975                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36976                 action : 'rotate-left',
36977                 cn : [
36978                     {
36979                         tag : 'button',
36980                         cls : 'btn btn-default',
36981                         html : '<i class="fa fa-undo"></i>'
36982                     }
36983                 ]
36984             },
36985             {
36986                 tag : 'div',
36987                 cls : 'btn-group roo-upload-cropbox-download',
36988                 action : 'download',
36989                 cn : [
36990                     {
36991                         tag : 'button',
36992                         cls : 'btn btn-default',
36993                         html : '<i class="fa fa-download"></i>'
36994                     }
36995                 ]
36996             },
36997             {
36998                 tag : 'div',
36999                 cls : 'btn-group roo-upload-cropbox-crop',
37000                 action : 'crop',
37001                 cn : [
37002                     {
37003                         tag : 'button',
37004                         cls : 'btn btn-default',
37005                         html : '<i class="fa fa-crop"></i>'
37006                     }
37007                 ]
37008             },
37009             {
37010                 tag : 'div',
37011                 cls : 'btn-group roo-upload-cropbox-trash',
37012                 action : 'trash',
37013                 cn : [
37014                     {
37015                         tag : 'button',
37016                         cls : 'btn btn-default',
37017                         html : '<i class="fa fa-trash"></i>'
37018                     }
37019                 ]
37020             },
37021             {
37022                 tag : 'div',
37023                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37024                 action : 'rotate-right',
37025                 cn : [
37026                     {
37027                         tag : 'button',
37028                         cls : 'btn btn-default',
37029                         html : '<i class="fa fa-repeat"></i>'
37030                     }
37031                 ]
37032             }
37033         ],
37034         ROTATOR : [
37035             {
37036                 tag : 'div',
37037                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37038                 action : 'rotate-left',
37039                 cn : [
37040                     {
37041                         tag : 'button',
37042                         cls : 'btn btn-default',
37043                         html : '<i class="fa fa-undo"></i>'
37044                     }
37045                 ]
37046             },
37047             {
37048                 tag : 'div',
37049                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37050                 action : 'rotate-right',
37051                 cn : [
37052                     {
37053                         tag : 'button',
37054                         cls : 'btn btn-default',
37055                         html : '<i class="fa fa-repeat"></i>'
37056                     }
37057                 ]
37058             }
37059         ]
37060     }
37061 });
37062
37063 /*
37064 * Licence: LGPL
37065 */
37066
37067 /**
37068  * @class Roo.bootstrap.DocumentManager
37069  * @extends Roo.bootstrap.Component
37070  * Bootstrap DocumentManager class
37071  * @cfg {String} paramName default 'imageUpload'
37072  * @cfg {String} toolTipName default 'filename'
37073  * @cfg {String} method default POST
37074  * @cfg {String} url action url
37075  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37076  * @cfg {Boolean} multiple multiple upload default true
37077  * @cfg {Number} thumbSize default 300
37078  * @cfg {String} fieldLabel
37079  * @cfg {Number} labelWidth default 4
37080  * @cfg {String} labelAlign (left|top) default left
37081  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37082 * @cfg {Number} labellg set the width of label (1-12)
37083  * @cfg {Number} labelmd set the width of label (1-12)
37084  * @cfg {Number} labelsm set the width of label (1-12)
37085  * @cfg {Number} labelxs set the width of label (1-12)
37086  * 
37087  * @constructor
37088  * Create a new DocumentManager
37089  * @param {Object} config The config object
37090  */
37091
37092 Roo.bootstrap.DocumentManager = function(config){
37093     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37094     
37095     this.files = [];
37096     this.delegates = [];
37097     
37098     this.addEvents({
37099         /**
37100          * @event initial
37101          * Fire when initial the DocumentManager
37102          * @param {Roo.bootstrap.DocumentManager} this
37103          */
37104         "initial" : true,
37105         /**
37106          * @event inspect
37107          * inspect selected file
37108          * @param {Roo.bootstrap.DocumentManager} this
37109          * @param {File} file
37110          */
37111         "inspect" : true,
37112         /**
37113          * @event exception
37114          * Fire when xhr load exception
37115          * @param {Roo.bootstrap.DocumentManager} this
37116          * @param {XMLHttpRequest} xhr
37117          */
37118         "exception" : true,
37119         /**
37120          * @event afterupload
37121          * Fire when xhr load exception
37122          * @param {Roo.bootstrap.DocumentManager} this
37123          * @param {XMLHttpRequest} xhr
37124          */
37125         "afterupload" : true,
37126         /**
37127          * @event prepare
37128          * prepare the form data
37129          * @param {Roo.bootstrap.DocumentManager} this
37130          * @param {Object} formData
37131          */
37132         "prepare" : true,
37133         /**
37134          * @event remove
37135          * Fire when remove the file
37136          * @param {Roo.bootstrap.DocumentManager} this
37137          * @param {Object} file
37138          */
37139         "remove" : true,
37140         /**
37141          * @event refresh
37142          * Fire after refresh the file
37143          * @param {Roo.bootstrap.DocumentManager} this
37144          */
37145         "refresh" : true,
37146         /**
37147          * @event click
37148          * Fire after click the image
37149          * @param {Roo.bootstrap.DocumentManager} this
37150          * @param {Object} file
37151          */
37152         "click" : true,
37153         /**
37154          * @event edit
37155          * Fire when upload a image and editable set to true
37156          * @param {Roo.bootstrap.DocumentManager} this
37157          * @param {Object} file
37158          */
37159         "edit" : true,
37160         /**
37161          * @event beforeselectfile
37162          * Fire before select file
37163          * @param {Roo.bootstrap.DocumentManager} this
37164          */
37165         "beforeselectfile" : true,
37166         /**
37167          * @event process
37168          * Fire before process file
37169          * @param {Roo.bootstrap.DocumentManager} this
37170          * @param {Object} file
37171          */
37172         "process" : true,
37173         /**
37174          * @event previewrendered
37175          * Fire when preview rendered
37176          * @param {Roo.bootstrap.DocumentManager} this
37177          * @param {Object} file
37178          */
37179         "previewrendered" : true,
37180         /**
37181          */
37182         "previewResize" : true
37183         
37184     });
37185 };
37186
37187 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
37188     
37189     boxes : 0,
37190     inputName : '',
37191     thumbSize : 300,
37192     multiple : true,
37193     files : false,
37194     method : 'POST',
37195     url : '',
37196     paramName : 'imageUpload',
37197     toolTipName : 'filename',
37198     fieldLabel : '',
37199     labelWidth : 4,
37200     labelAlign : 'left',
37201     editable : true,
37202     delegates : false,
37203     xhr : false, 
37204     
37205     labellg : 0,
37206     labelmd : 0,
37207     labelsm : 0,
37208     labelxs : 0,
37209     
37210     getAutoCreate : function()
37211     {   
37212         var managerWidget = {
37213             tag : 'div',
37214             cls : 'roo-document-manager',
37215             cn : [
37216                 {
37217                     tag : 'input',
37218                     cls : 'roo-document-manager-selector',
37219                     type : 'file'
37220                 },
37221                 {
37222                     tag : 'div',
37223                     cls : 'roo-document-manager-uploader',
37224                     cn : [
37225                         {
37226                             tag : 'div',
37227                             cls : 'roo-document-manager-upload-btn',
37228                             html : '<i class="fa fa-plus"></i>'
37229                         }
37230                     ]
37231                     
37232                 }
37233             ]
37234         };
37235         
37236         var content = [
37237             {
37238                 tag : 'div',
37239                 cls : 'column col-md-12',
37240                 cn : managerWidget
37241             }
37242         ];
37243         
37244         if(this.fieldLabel.length){
37245             
37246             content = [
37247                 {
37248                     tag : 'div',
37249                     cls : 'column col-md-12',
37250                     html : this.fieldLabel
37251                 },
37252                 {
37253                     tag : 'div',
37254                     cls : 'column col-md-12',
37255                     cn : managerWidget
37256                 }
37257             ];
37258
37259             if(this.labelAlign == 'left'){
37260                 content = [
37261                     {
37262                         tag : 'div',
37263                         cls : 'column',
37264                         html : this.fieldLabel
37265                     },
37266                     {
37267                         tag : 'div',
37268                         cls : 'column',
37269                         cn : managerWidget
37270                     }
37271                 ];
37272                 
37273                 if(this.labelWidth > 12){
37274                     content[0].style = "width: " + this.labelWidth + 'px';
37275                 }
37276
37277                 if(this.labelWidth < 13 && this.labelmd == 0){
37278                     this.labelmd = this.labelWidth;
37279                 }
37280
37281                 if(this.labellg > 0){
37282                     content[0].cls += ' col-lg-' + this.labellg;
37283                     content[1].cls += ' col-lg-' + (12 - this.labellg);
37284                 }
37285
37286                 if(this.labelmd > 0){
37287                     content[0].cls += ' col-md-' + this.labelmd;
37288                     content[1].cls += ' col-md-' + (12 - this.labelmd);
37289                 }
37290
37291                 if(this.labelsm > 0){
37292                     content[0].cls += ' col-sm-' + this.labelsm;
37293                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
37294                 }
37295
37296                 if(this.labelxs > 0){
37297                     content[0].cls += ' col-xs-' + this.labelxs;
37298                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
37299                 }
37300                 
37301             }
37302         }
37303         
37304         var cfg = {
37305             tag : 'div',
37306             cls : 'row clearfix',
37307             cn : content
37308         };
37309         
37310         return cfg;
37311         
37312     },
37313     
37314     initEvents : function()
37315     {
37316         this.managerEl = this.el.select('.roo-document-manager', true).first();
37317         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37318         
37319         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37320         this.selectorEl.hide();
37321         
37322         if(this.multiple){
37323             this.selectorEl.attr('multiple', 'multiple');
37324         }
37325         
37326         this.selectorEl.on('change', this.onFileSelected, this);
37327         
37328         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37329         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37330         
37331         this.uploader.on('click', this.onUploaderClick, this);
37332         
37333         this.renderProgressDialog();
37334         
37335         var _this = this;
37336         
37337         window.addEventListener("resize", function() { _this.refresh(); } );
37338         
37339         this.fireEvent('initial', this);
37340     },
37341     
37342     renderProgressDialog : function()
37343     {
37344         var _this = this;
37345         
37346         this.progressDialog = new Roo.bootstrap.Modal({
37347             cls : 'roo-document-manager-progress-dialog',
37348             allow_close : false,
37349             animate : false,
37350             title : '',
37351             buttons : [
37352                 {
37353                     name  :'cancel',
37354                     weight : 'danger',
37355                     html : 'Cancel'
37356                 }
37357             ], 
37358             listeners : { 
37359                 btnclick : function() {
37360                     _this.uploadCancel();
37361                     this.hide();
37362                 }
37363             }
37364         });
37365          
37366         this.progressDialog.render(Roo.get(document.body));
37367          
37368         this.progress = new Roo.bootstrap.Progress({
37369             cls : 'roo-document-manager-progress',
37370             active : true,
37371             striped : true
37372         });
37373         
37374         this.progress.render(this.progressDialog.getChildContainer());
37375         
37376         this.progressBar = new Roo.bootstrap.ProgressBar({
37377             cls : 'roo-document-manager-progress-bar',
37378             aria_valuenow : 0,
37379             aria_valuemin : 0,
37380             aria_valuemax : 12,
37381             panel : 'success'
37382         });
37383         
37384         this.progressBar.render(this.progress.getChildContainer());
37385     },
37386     
37387     onUploaderClick : function(e)
37388     {
37389         e.preventDefault();
37390      
37391         if(this.fireEvent('beforeselectfile', this) != false){
37392             this.selectorEl.dom.click();
37393         }
37394         
37395     },
37396     
37397     onFileSelected : function(e)
37398     {
37399         e.preventDefault();
37400         
37401         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37402             return;
37403         }
37404         
37405         Roo.each(this.selectorEl.dom.files, function(file){
37406             if(this.fireEvent('inspect', this, file) != false){
37407                 this.files.push(file);
37408             }
37409         }, this);
37410         
37411         this.queue();
37412         
37413     },
37414     
37415     queue : function()
37416     {
37417         this.selectorEl.dom.value = '';
37418         
37419         if(!this.files || !this.files.length){
37420             return;
37421         }
37422         
37423         if(this.boxes > 0 && this.files.length > this.boxes){
37424             this.files = this.files.slice(0, this.boxes);
37425         }
37426         
37427         this.uploader.show();
37428         
37429         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37430             this.uploader.hide();
37431         }
37432         
37433         var _this = this;
37434         
37435         var files = [];
37436         
37437         var docs = [];
37438         
37439         Roo.each(this.files, function(file){
37440             
37441             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37442                 var f = this.renderPreview(file);
37443                 files.push(f);
37444                 return;
37445             }
37446             
37447             if(file.type.indexOf('image') != -1){
37448                 this.delegates.push(
37449                     (function(){
37450                         _this.process(file);
37451                     }).createDelegate(this)
37452                 );
37453         
37454                 return;
37455             }
37456             
37457             docs.push(
37458                 (function(){
37459                     _this.process(file);
37460                 }).createDelegate(this)
37461             );
37462             
37463         }, this);
37464         
37465         this.files = files;
37466         
37467         this.delegates = this.delegates.concat(docs);
37468         
37469         if(!this.delegates.length){
37470             this.refresh();
37471             return;
37472         }
37473         
37474         this.progressBar.aria_valuemax = this.delegates.length;
37475         
37476         this.arrange();
37477         
37478         return;
37479     },
37480     
37481     arrange : function()
37482     {
37483         if(!this.delegates.length){
37484             this.progressDialog.hide();
37485             this.refresh();
37486             return;
37487         }
37488         
37489         var delegate = this.delegates.shift();
37490         
37491         this.progressDialog.show();
37492         
37493         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37494         
37495         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37496         
37497         delegate();
37498     },
37499     
37500     refresh : function()
37501     {
37502         this.uploader.show();
37503         
37504         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37505             this.uploader.hide();
37506         }
37507         
37508         Roo.isTouch ? this.closable(false) : this.closable(true);
37509         
37510         this.fireEvent('refresh', this);
37511     },
37512     
37513     onRemove : function(e, el, o)
37514     {
37515         e.preventDefault();
37516         
37517         this.fireEvent('remove', this, o);
37518         
37519     },
37520     
37521     remove : function(o)
37522     {
37523         var files = [];
37524         
37525         Roo.each(this.files, function(file){
37526             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37527                 files.push(file);
37528                 return;
37529             }
37530
37531             o.target.remove();
37532
37533         }, this);
37534         
37535         this.files = files;
37536         
37537         this.refresh();
37538     },
37539     
37540     clear : function()
37541     {
37542         Roo.each(this.files, function(file){
37543             if(!file.target){
37544                 return;
37545             }
37546             
37547             file.target.remove();
37548
37549         }, this);
37550         
37551         this.files = [];
37552         
37553         this.refresh();
37554     },
37555     
37556     onClick : function(e, el, o)
37557     {
37558         e.preventDefault();
37559         
37560         this.fireEvent('click', this, o);
37561         
37562     },
37563     
37564     closable : function(closable)
37565     {
37566         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37567             
37568             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37569             
37570             if(closable){
37571                 el.show();
37572                 return;
37573             }
37574             
37575             el.hide();
37576             
37577         }, this);
37578     },
37579     
37580     xhrOnLoad : function(xhr)
37581     {
37582         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37583             el.remove();
37584         }, this);
37585         
37586         if (xhr.readyState !== 4) {
37587             this.arrange();
37588             this.fireEvent('exception', this, xhr);
37589             return;
37590         }
37591
37592         var response = Roo.decode(xhr.responseText);
37593         
37594         if(!response.success){
37595             this.arrange();
37596             this.fireEvent('exception', this, xhr);
37597             return;
37598         }
37599         
37600         var file = this.renderPreview(response.data);
37601         
37602         this.files.push(file);
37603         
37604         this.arrange();
37605         
37606         this.fireEvent('afterupload', this, xhr);
37607         
37608     },
37609     
37610     xhrOnError : function(xhr)
37611     {
37612         Roo.log('xhr on error');
37613         
37614         var response = Roo.decode(xhr.responseText);
37615           
37616         Roo.log(response);
37617         
37618         this.arrange();
37619     },
37620     
37621     process : function(file)
37622     {
37623         if(this.fireEvent('process', this, file) !== false){
37624             if(this.editable && file.type.indexOf('image') != -1){
37625                 this.fireEvent('edit', this, file);
37626                 return;
37627             }
37628
37629             this.uploadStart(file, false);
37630
37631             return;
37632         }
37633         
37634     },
37635     
37636     uploadStart : function(file, crop)
37637     {
37638         this.xhr = new XMLHttpRequest();
37639         
37640         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37641             this.arrange();
37642             return;
37643         }
37644         
37645         file.xhr = this.xhr;
37646             
37647         this.managerEl.createChild({
37648             tag : 'div',
37649             cls : 'roo-document-manager-loading',
37650             cn : [
37651                 {
37652                     tag : 'div',
37653                     tooltip : file.name,
37654                     cls : 'roo-document-manager-thumb',
37655                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37656                 }
37657             ]
37658
37659         });
37660
37661         this.xhr.open(this.method, this.url, true);
37662         
37663         var headers = {
37664             "Accept": "application/json",
37665             "Cache-Control": "no-cache",
37666             "X-Requested-With": "XMLHttpRequest"
37667         };
37668         
37669         for (var headerName in headers) {
37670             var headerValue = headers[headerName];
37671             if (headerValue) {
37672                 this.xhr.setRequestHeader(headerName, headerValue);
37673             }
37674         }
37675         
37676         var _this = this;
37677         
37678         this.xhr.onload = function()
37679         {
37680             _this.xhrOnLoad(_this.xhr);
37681         }
37682         
37683         this.xhr.onerror = function()
37684         {
37685             _this.xhrOnError(_this.xhr);
37686         }
37687         
37688         var formData = new FormData();
37689
37690         formData.append('returnHTML', 'NO');
37691         
37692         if(crop){
37693             formData.append('crop', crop);
37694         }
37695         
37696         formData.append(this.paramName, file, file.name);
37697         
37698         var options = {
37699             file : file, 
37700             manually : false
37701         };
37702         
37703         if(this.fireEvent('prepare', this, formData, options) != false){
37704             
37705             if(options.manually){
37706                 return;
37707             }
37708             
37709             this.xhr.send(formData);
37710             return;
37711         };
37712         
37713         this.uploadCancel();
37714     },
37715     
37716     uploadCancel : function()
37717     {
37718         if (this.xhr) {
37719             this.xhr.abort();
37720         }
37721         
37722         this.delegates = [];
37723         
37724         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37725             el.remove();
37726         }, this);
37727         
37728         this.arrange();
37729     },
37730     
37731     renderPreview : function(file)
37732     {
37733         if(typeof(file.target) != 'undefined' && file.target){
37734             return file;
37735         }
37736         
37737         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37738         
37739         var previewEl = this.managerEl.createChild({
37740             tag : 'div',
37741             cls : 'roo-document-manager-preview',
37742             cn : [
37743                 {
37744                     tag : 'div',
37745                     tooltip : file[this.toolTipName],
37746                     cls : 'roo-document-manager-thumb',
37747                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37748                 },
37749                 {
37750                     tag : 'button',
37751                     cls : 'close',
37752                     html : '<i class="fa fa-times-circle"></i>'
37753                 }
37754             ]
37755         });
37756
37757         var close = previewEl.select('button.close', true).first();
37758
37759         close.on('click', this.onRemove, this, file);
37760
37761         file.target = previewEl;
37762
37763         var image = previewEl.select('img', true).first();
37764         
37765         var _this = this;
37766         
37767         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37768         
37769         image.on('click', this.onClick, this, file);
37770         
37771         this.fireEvent('previewrendered', this, file);
37772         
37773         return file;
37774         
37775     },
37776     
37777     onPreviewLoad : function(file, image)
37778     {
37779         if(typeof(file.target) == 'undefined' || !file.target){
37780             return;
37781         }
37782         
37783         var width = image.dom.naturalWidth || image.dom.width;
37784         var height = image.dom.naturalHeight || image.dom.height;
37785         
37786         if(!this.previewResize) {
37787             return;
37788         }
37789         
37790         if(width > height){
37791             file.target.addClass('wide');
37792             return;
37793         }
37794         
37795         file.target.addClass('tall');
37796         return;
37797         
37798     },
37799     
37800     uploadFromSource : function(file, crop)
37801     {
37802         this.xhr = new XMLHttpRequest();
37803         
37804         this.managerEl.createChild({
37805             tag : 'div',
37806             cls : 'roo-document-manager-loading',
37807             cn : [
37808                 {
37809                     tag : 'div',
37810                     tooltip : file.name,
37811                     cls : 'roo-document-manager-thumb',
37812                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37813                 }
37814             ]
37815
37816         });
37817
37818         this.xhr.open(this.method, this.url, true);
37819         
37820         var headers = {
37821             "Accept": "application/json",
37822             "Cache-Control": "no-cache",
37823             "X-Requested-With": "XMLHttpRequest"
37824         };
37825         
37826         for (var headerName in headers) {
37827             var headerValue = headers[headerName];
37828             if (headerValue) {
37829                 this.xhr.setRequestHeader(headerName, headerValue);
37830             }
37831         }
37832         
37833         var _this = this;
37834         
37835         this.xhr.onload = function()
37836         {
37837             _this.xhrOnLoad(_this.xhr);
37838         }
37839         
37840         this.xhr.onerror = function()
37841         {
37842             _this.xhrOnError(_this.xhr);
37843         }
37844         
37845         var formData = new FormData();
37846
37847         formData.append('returnHTML', 'NO');
37848         
37849         formData.append('crop', crop);
37850         
37851         if(typeof(file.filename) != 'undefined'){
37852             formData.append('filename', file.filename);
37853         }
37854         
37855         if(typeof(file.mimetype) != 'undefined'){
37856             formData.append('mimetype', file.mimetype);
37857         }
37858         
37859         Roo.log(formData);
37860         
37861         if(this.fireEvent('prepare', this, formData) != false){
37862             this.xhr.send(formData);
37863         };
37864     }
37865 });
37866
37867 /*
37868 * Licence: LGPL
37869 */
37870
37871 /**
37872  * @class Roo.bootstrap.DocumentViewer
37873  * @extends Roo.bootstrap.Component
37874  * Bootstrap DocumentViewer class
37875  * @cfg {Boolean} showDownload (true|false) show download button (default true)
37876  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37877  * 
37878  * @constructor
37879  * Create a new DocumentViewer
37880  * @param {Object} config The config object
37881  */
37882
37883 Roo.bootstrap.DocumentViewer = function(config){
37884     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37885     
37886     this.addEvents({
37887         /**
37888          * @event initial
37889          * Fire after initEvent
37890          * @param {Roo.bootstrap.DocumentViewer} this
37891          */
37892         "initial" : true,
37893         /**
37894          * @event click
37895          * Fire after click
37896          * @param {Roo.bootstrap.DocumentViewer} this
37897          */
37898         "click" : true,
37899         /**
37900          * @event download
37901          * Fire after download button
37902          * @param {Roo.bootstrap.DocumentViewer} this
37903          */
37904         "download" : true,
37905         /**
37906          * @event trash
37907          * Fire after trash button
37908          * @param {Roo.bootstrap.DocumentViewer} this
37909          */
37910         "trash" : true
37911         
37912     });
37913 };
37914
37915 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
37916     
37917     showDownload : true,
37918     
37919     showTrash : true,
37920     
37921     getAutoCreate : function()
37922     {
37923         var cfg = {
37924             tag : 'div',
37925             cls : 'roo-document-viewer',
37926             cn : [
37927                 {
37928                     tag : 'div',
37929                     cls : 'roo-document-viewer-body',
37930                     cn : [
37931                         {
37932                             tag : 'div',
37933                             cls : 'roo-document-viewer-thumb',
37934                             cn : [
37935                                 {
37936                                     tag : 'img',
37937                                     cls : 'roo-document-viewer-image'
37938                                 }
37939                             ]
37940                         }
37941                     ]
37942                 },
37943                 {
37944                     tag : 'div',
37945                     cls : 'roo-document-viewer-footer',
37946                     cn : {
37947                         tag : 'div',
37948                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37949                         cn : [
37950                             {
37951                                 tag : 'div',
37952                                 cls : 'btn-group roo-document-viewer-download',
37953                                 cn : [
37954                                     {
37955                                         tag : 'button',
37956                                         cls : 'btn btn-default',
37957                                         html : '<i class="fa fa-download"></i>'
37958                                     }
37959                                 ]
37960                             },
37961                             {
37962                                 tag : 'div',
37963                                 cls : 'btn-group roo-document-viewer-trash',
37964                                 cn : [
37965                                     {
37966                                         tag : 'button',
37967                                         cls : 'btn btn-default',
37968                                         html : '<i class="fa fa-trash"></i>'
37969                                     }
37970                                 ]
37971                             }
37972                         ]
37973                     }
37974                 }
37975             ]
37976         };
37977         
37978         return cfg;
37979     },
37980     
37981     initEvents : function()
37982     {
37983         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
37984         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37985         
37986         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
37987         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37988         
37989         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
37990         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37991         
37992         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
37993         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
37994         
37995         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
37996         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
37997         
37998         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
37999         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38000         
38001         this.bodyEl.on('click', this.onClick, this);
38002         this.downloadBtn.on('click', this.onDownload, this);
38003         this.trashBtn.on('click', this.onTrash, this);
38004         
38005         this.downloadBtn.hide();
38006         this.trashBtn.hide();
38007         
38008         if(this.showDownload){
38009             this.downloadBtn.show();
38010         }
38011         
38012         if(this.showTrash){
38013             this.trashBtn.show();
38014         }
38015         
38016         if(!this.showDownload && !this.showTrash) {
38017             this.footerEl.hide();
38018         }
38019         
38020     },
38021     
38022     initial : function()
38023     {
38024         this.fireEvent('initial', this);
38025         
38026     },
38027     
38028     onClick : function(e)
38029     {
38030         e.preventDefault();
38031         
38032         this.fireEvent('click', this);
38033     },
38034     
38035     onDownload : function(e)
38036     {
38037         e.preventDefault();
38038         
38039         this.fireEvent('download', this);
38040     },
38041     
38042     onTrash : function(e)
38043     {
38044         e.preventDefault();
38045         
38046         this.fireEvent('trash', this);
38047     }
38048     
38049 });
38050 /*
38051  * - LGPL
38052  *
38053  * FieldLabel
38054  * 
38055  */
38056
38057 /**
38058  * @class Roo.bootstrap.form.FieldLabel
38059  * @extends Roo.bootstrap.Component
38060  * Bootstrap FieldLabel class
38061  * @cfg {String} html contents of the element
38062  * @cfg {String} tag tag of the element default label
38063  * @cfg {String} cls class of the element
38064  * @cfg {String} target label target 
38065  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38066  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38067  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38068  * @cfg {String} iconTooltip default "This field is required"
38069  * @cfg {String} indicatorpos (left|right) default left
38070  * 
38071  * @constructor
38072  * Create a new FieldLabel
38073  * @param {Object} config The config object
38074  */
38075
38076 Roo.bootstrap.form.FieldLabel = function(config){
38077     Roo.bootstrap.Element.superclass.constructor.call(this, config);
38078     
38079     this.addEvents({
38080             /**
38081              * @event invalid
38082              * Fires after the field has been marked as invalid.
38083              * @param {Roo.form.FieldLabel} this
38084              * @param {String} msg The validation message
38085              */
38086             invalid : true,
38087             /**
38088              * @event valid
38089              * Fires after the field has been validated with no errors.
38090              * @param {Roo.form.FieldLabel} this
38091              */
38092             valid : true
38093         });
38094 };
38095
38096 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
38097     
38098     tag: 'label',
38099     cls: '',
38100     html: '',
38101     target: '',
38102     allowBlank : true,
38103     invalidClass : 'has-warning',
38104     validClass : 'has-success',
38105     iconTooltip : 'This field is required',
38106     indicatorpos : 'left',
38107     
38108     getAutoCreate : function(){
38109         
38110         var cls = "";
38111         if (!this.allowBlank) {
38112             cls  = "visible";
38113         }
38114         
38115         var cfg = {
38116             tag : this.tag,
38117             cls : 'roo-bootstrap-field-label ' + this.cls,
38118             for : this.target,
38119             cn : [
38120                 {
38121                     tag : 'i',
38122                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38123                     tooltip : this.iconTooltip
38124                 },
38125                 {
38126                     tag : 'span',
38127                     html : this.html
38128                 }
38129             ] 
38130         };
38131         
38132         if(this.indicatorpos == 'right'){
38133             var cfg = {
38134                 tag : this.tag,
38135                 cls : 'roo-bootstrap-field-label ' + this.cls,
38136                 for : this.target,
38137                 cn : [
38138                     {
38139                         tag : 'span',
38140                         html : this.html
38141                     },
38142                     {
38143                         tag : 'i',
38144                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38145                         tooltip : this.iconTooltip
38146                     }
38147                 ] 
38148             };
38149         }
38150         
38151         return cfg;
38152     },
38153     
38154     initEvents: function() 
38155     {
38156         Roo.bootstrap.Element.superclass.initEvents.call(this);
38157         
38158         this.indicator = this.indicatorEl();
38159         
38160         if(this.indicator){
38161             this.indicator.removeClass('visible');
38162             this.indicator.addClass('invisible');
38163         }
38164         
38165         Roo.bootstrap.form.FieldLabel.register(this);
38166     },
38167     
38168     indicatorEl : function()
38169     {
38170         var indicator = this.el.select('i.roo-required-indicator',true).first();
38171         
38172         if(!indicator){
38173             return false;
38174         }
38175         
38176         return indicator;
38177         
38178     },
38179     
38180     /**
38181      * Mark this field as valid
38182      */
38183     markValid : function()
38184     {
38185         if(this.indicator){
38186             this.indicator.removeClass('visible');
38187             this.indicator.addClass('invisible');
38188         }
38189         if (Roo.bootstrap.version == 3) {
38190             this.el.removeClass(this.invalidClass);
38191             this.el.addClass(this.validClass);
38192         } else {
38193             this.el.removeClass('is-invalid');
38194             this.el.addClass('is-valid');
38195         }
38196         
38197         
38198         this.fireEvent('valid', this);
38199     },
38200     
38201     /**
38202      * Mark this field as invalid
38203      * @param {String} msg The validation message
38204      */
38205     markInvalid : function(msg)
38206     {
38207         if(this.indicator){
38208             this.indicator.removeClass('invisible');
38209             this.indicator.addClass('visible');
38210         }
38211           if (Roo.bootstrap.version == 3) {
38212             this.el.removeClass(this.validClass);
38213             this.el.addClass(this.invalidClass);
38214         } else {
38215             this.el.removeClass('is-valid');
38216             this.el.addClass('is-invalid');
38217         }
38218         
38219         
38220         this.fireEvent('invalid', this, msg);
38221     }
38222     
38223    
38224 });
38225
38226 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38227     
38228     groups: {},
38229     
38230      /**
38231     * register a FieldLabel Group
38232     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38233     */
38234     register : function(label)
38235     {
38236         if(this.groups.hasOwnProperty(label.target)){
38237             return;
38238         }
38239      
38240         this.groups[label.target] = label;
38241         
38242     },
38243     /**
38244     * fetch a FieldLabel Group based on the target
38245     * @param {string} target
38246     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38247     */
38248     get: function(target) {
38249         if (typeof(this.groups[target]) == 'undefined') {
38250             return false;
38251         }
38252         
38253         return this.groups[target] ;
38254     }
38255 });
38256
38257  
38258
38259  /*
38260  * - LGPL
38261  *
38262  * page DateSplitField.
38263  * 
38264  */
38265
38266
38267 /**
38268  * @class Roo.bootstrap.form.DateSplitField
38269  * @extends Roo.bootstrap.Component
38270  * Bootstrap DateSplitField class
38271  * @cfg {string} fieldLabel - the label associated
38272  * @cfg {Number} labelWidth set the width of label (0-12)
38273  * @cfg {String} labelAlign (top|left)
38274  * @cfg {Boolean} dayAllowBlank (true|false) default false
38275  * @cfg {Boolean} monthAllowBlank (true|false) default false
38276  * @cfg {Boolean} yearAllowBlank (true|false) default false
38277  * @cfg {string} dayPlaceholder 
38278  * @cfg {string} monthPlaceholder
38279  * @cfg {string} yearPlaceholder
38280  * @cfg {string} dayFormat default 'd'
38281  * @cfg {string} monthFormat default 'm'
38282  * @cfg {string} yearFormat default 'Y'
38283  * @cfg {Number} labellg set the width of label (1-12)
38284  * @cfg {Number} labelmd set the width of label (1-12)
38285  * @cfg {Number} labelsm set the width of label (1-12)
38286  * @cfg {Number} labelxs set the width of label (1-12)
38287
38288  *     
38289  * @constructor
38290  * Create a new DateSplitField
38291  * @param {Object} config The config object
38292  */
38293
38294 Roo.bootstrap.form.DateSplitField = function(config){
38295     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38296     
38297     this.addEvents({
38298         // raw events
38299          /**
38300          * @event years
38301          * getting the data of years
38302          * @param {Roo.bootstrap.form.DateSplitField} this
38303          * @param {Object} years
38304          */
38305         "years" : true,
38306         /**
38307          * @event days
38308          * getting the data of days
38309          * @param {Roo.bootstrap.form.DateSplitField} this
38310          * @param {Object} days
38311          */
38312         "days" : true,
38313         /**
38314          * @event invalid
38315          * Fires after the field has been marked as invalid.
38316          * @param {Roo.form.Field} this
38317          * @param {String} msg The validation message
38318          */
38319         invalid : true,
38320        /**
38321          * @event valid
38322          * Fires after the field has been validated with no errors.
38323          * @param {Roo.form.Field} this
38324          */
38325         valid : true
38326     });
38327 };
38328
38329 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
38330     
38331     fieldLabel : '',
38332     labelAlign : 'top',
38333     labelWidth : 3,
38334     dayAllowBlank : false,
38335     monthAllowBlank : false,
38336     yearAllowBlank : false,
38337     dayPlaceholder : '',
38338     monthPlaceholder : '',
38339     yearPlaceholder : '',
38340     dayFormat : 'd',
38341     monthFormat : 'm',
38342     yearFormat : 'Y',
38343     isFormField : true,
38344     labellg : 0,
38345     labelmd : 0,
38346     labelsm : 0,
38347     labelxs : 0,
38348     
38349     getAutoCreate : function()
38350     {
38351         var cfg = {
38352             tag : 'div',
38353             cls : 'row roo-date-split-field-group',
38354             cn : [
38355                 {
38356                     tag : 'input',
38357                     type : 'hidden',
38358                     cls : 'form-hidden-field roo-date-split-field-group-value',
38359                     name : this.name
38360                 }
38361             ]
38362         };
38363         
38364         var labelCls = 'col-md-12';
38365         var contentCls = 'col-md-4';
38366         
38367         if(this.fieldLabel){
38368             
38369             var label = {
38370                 tag : 'div',
38371                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38372                 cn : [
38373                     {
38374                         tag : 'label',
38375                         html : this.fieldLabel
38376                     }
38377                 ]
38378             };
38379             
38380             if(this.labelAlign == 'left'){
38381             
38382                 if(this.labelWidth > 12){
38383                     label.style = "width: " + this.labelWidth + 'px';
38384                 }
38385
38386                 if(this.labelWidth < 13 && this.labelmd == 0){
38387                     this.labelmd = this.labelWidth;
38388                 }
38389
38390                 if(this.labellg > 0){
38391                     labelCls = ' col-lg-' + this.labellg;
38392                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38393                 }
38394
38395                 if(this.labelmd > 0){
38396                     labelCls = ' col-md-' + this.labelmd;
38397                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38398                 }
38399
38400                 if(this.labelsm > 0){
38401                     labelCls = ' col-sm-' + this.labelsm;
38402                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38403                 }
38404
38405                 if(this.labelxs > 0){
38406                     labelCls = ' col-xs-' + this.labelxs;
38407                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38408                 }
38409             }
38410             
38411             label.cls += ' ' + labelCls;
38412             
38413             cfg.cn.push(label);
38414         }
38415         
38416         Roo.each(['day', 'month', 'year'], function(t){
38417             cfg.cn.push({
38418                 tag : 'div',
38419                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38420             });
38421         }, this);
38422         
38423         return cfg;
38424     },
38425     
38426     inputEl: function ()
38427     {
38428         return this.el.select('.roo-date-split-field-group-value', true).first();
38429     },
38430     
38431     onRender : function(ct, position) 
38432     {
38433         var _this = this;
38434         
38435         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38436         
38437         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38438         
38439         this.dayField = new Roo.bootstrap.form.ComboBox({
38440             allowBlank : this.dayAllowBlank,
38441             alwaysQuery : true,
38442             displayField : 'value',
38443             editable : false,
38444             fieldLabel : '',
38445             forceSelection : true,
38446             mode : 'local',
38447             placeholder : this.dayPlaceholder,
38448             selectOnFocus : true,
38449             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38450             triggerAction : 'all',
38451             typeAhead : true,
38452             valueField : 'value',
38453             store : new Roo.data.SimpleStore({
38454                 data : (function() {    
38455                     var days = [];
38456                     _this.fireEvent('days', _this, days);
38457                     return days;
38458                 })(),
38459                 fields : [ 'value' ]
38460             }),
38461             listeners : {
38462                 select : function (_self, record, index)
38463                 {
38464                     _this.setValue(_this.getValue());
38465                 }
38466             }
38467         });
38468
38469         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38470         
38471         this.monthField = new Roo.bootstrap.form.MonthField({
38472             after : '<i class=\"fa fa-calendar\"></i>',
38473             allowBlank : this.monthAllowBlank,
38474             placeholder : this.monthPlaceholder,
38475             readOnly : true,
38476             listeners : {
38477                 render : function (_self)
38478                 {
38479                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
38480                         e.preventDefault();
38481                         _self.focus();
38482                     });
38483                 },
38484                 select : function (_self, oldvalue, newvalue)
38485                 {
38486                     _this.setValue(_this.getValue());
38487                 }
38488             }
38489         });
38490         
38491         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38492         
38493         this.yearField = new Roo.bootstrap.form.ComboBox({
38494             allowBlank : this.yearAllowBlank,
38495             alwaysQuery : true,
38496             displayField : 'value',
38497             editable : false,
38498             fieldLabel : '',
38499             forceSelection : true,
38500             mode : 'local',
38501             placeholder : this.yearPlaceholder,
38502             selectOnFocus : true,
38503             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38504             triggerAction : 'all',
38505             typeAhead : true,
38506             valueField : 'value',
38507             store : new Roo.data.SimpleStore({
38508                 data : (function() {
38509                     var years = [];
38510                     _this.fireEvent('years', _this, years);
38511                     return years;
38512                 })(),
38513                 fields : [ 'value' ]
38514             }),
38515             listeners : {
38516                 select : function (_self, record, index)
38517                 {
38518                     _this.setValue(_this.getValue());
38519                 }
38520             }
38521         });
38522
38523         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38524     },
38525     
38526     setValue : function(v, format)
38527     {
38528         this.inputEl.dom.value = v;
38529         
38530         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38531         
38532         var d = Date.parseDate(v, f);
38533         
38534         if(!d){
38535             this.validate();
38536             return;
38537         }
38538         
38539         this.setDay(d.format(this.dayFormat));
38540         this.setMonth(d.format(this.monthFormat));
38541         this.setYear(d.format(this.yearFormat));
38542         
38543         this.validate();
38544         
38545         return;
38546     },
38547     
38548     setDay : function(v)
38549     {
38550         this.dayField.setValue(v);
38551         this.inputEl.dom.value = this.getValue();
38552         this.validate();
38553         return;
38554     },
38555     
38556     setMonth : function(v)
38557     {
38558         this.monthField.setValue(v, true);
38559         this.inputEl.dom.value = this.getValue();
38560         this.validate();
38561         return;
38562     },
38563     
38564     setYear : function(v)
38565     {
38566         this.yearField.setValue(v);
38567         this.inputEl.dom.value = this.getValue();
38568         this.validate();
38569         return;
38570     },
38571     
38572     getDay : function()
38573     {
38574         return this.dayField.getValue();
38575     },
38576     
38577     getMonth : function()
38578     {
38579         return this.monthField.getValue();
38580     },
38581     
38582     getYear : function()
38583     {
38584         return this.yearField.getValue();
38585     },
38586     
38587     getValue : function()
38588     {
38589         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38590         
38591         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38592         
38593         return date;
38594     },
38595     
38596     reset : function()
38597     {
38598         this.setDay('');
38599         this.setMonth('');
38600         this.setYear('');
38601         this.inputEl.dom.value = '';
38602         this.validate();
38603         return;
38604     },
38605     
38606     validate : function()
38607     {
38608         var d = this.dayField.validate();
38609         var m = this.monthField.validate();
38610         var y = this.yearField.validate();
38611         
38612         var valid = true;
38613         
38614         if(
38615                 (!this.dayAllowBlank && !d) ||
38616                 (!this.monthAllowBlank && !m) ||
38617                 (!this.yearAllowBlank && !y)
38618         ){
38619             valid = false;
38620         }
38621         
38622         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38623             return valid;
38624         }
38625         
38626         if(valid){
38627             this.markValid();
38628             return valid;
38629         }
38630         
38631         this.markInvalid();
38632         
38633         return valid;
38634     },
38635     
38636     markValid : function()
38637     {
38638         
38639         var label = this.el.select('label', true).first();
38640         var icon = this.el.select('i.fa-star', true).first();
38641
38642         if(label && icon){
38643             icon.remove();
38644         }
38645         
38646         this.fireEvent('valid', this);
38647     },
38648     
38649      /**
38650      * Mark this field as invalid
38651      * @param {String} msg The validation message
38652      */
38653     markInvalid : function(msg)
38654     {
38655         
38656         var label = this.el.select('label', true).first();
38657         var icon = this.el.select('i.fa-star', true).first();
38658
38659         if(label && !icon){
38660             this.el.select('.roo-date-split-field-label', true).createChild({
38661                 tag : 'i',
38662                 cls : 'text-danger fa fa-lg fa-star',
38663                 tooltip : 'This field is required',
38664                 style : 'margin-right:5px;'
38665             }, label, true);
38666         }
38667         
38668         this.fireEvent('invalid', this, msg);
38669     },
38670     
38671     clearInvalid : function()
38672     {
38673         var label = this.el.select('label', true).first();
38674         var icon = this.el.select('i.fa-star', true).first();
38675
38676         if(label && icon){
38677             icon.remove();
38678         }
38679         
38680         this.fireEvent('valid', this);
38681     },
38682     
38683     getName: function()
38684     {
38685         return this.name;
38686     }
38687     
38688 });
38689
38690  
38691
38692 /**
38693  * @class Roo.bootstrap.LayoutMasonry
38694  * @extends Roo.bootstrap.Component
38695  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38696  * Bootstrap Layout Masonry class
38697  *
38698  * This is based on 
38699  * http://masonry.desandro.com
38700  *
38701  * The idea is to render all the bricks based on vertical width...
38702  *
38703  * The original code extends 'outlayer' - we might need to use that....
38704
38705  * @constructor
38706  * Create a new Element
38707  * @param {Object} config The config object
38708  */
38709
38710 Roo.bootstrap.LayoutMasonry = function(config){
38711     
38712     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38713     
38714     this.bricks = [];
38715     
38716     Roo.bootstrap.LayoutMasonry.register(this);
38717     
38718     this.addEvents({
38719         // raw events
38720         /**
38721          * @event layout
38722          * Fire after layout the items
38723          * @param {Roo.bootstrap.LayoutMasonry} this
38724          * @param {Roo.EventObject} e
38725          */
38726         "layout" : true
38727     });
38728     
38729 };
38730
38731 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
38732     
38733     /**
38734      * @cfg {Boolean} isLayoutInstant = no animation?
38735      */   
38736     isLayoutInstant : false, // needed?
38737    
38738     /**
38739      * @cfg {Number} boxWidth  width of the columns
38740      */   
38741     boxWidth : 450,
38742     
38743       /**
38744      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
38745      */   
38746     boxHeight : 0,
38747     
38748     /**
38749      * @cfg {Number} padWidth padding below box..
38750      */   
38751     padWidth : 10, 
38752     
38753     /**
38754      * @cfg {Number} gutter gutter width..
38755      */   
38756     gutter : 10,
38757     
38758      /**
38759      * @cfg {Number} maxCols maximum number of columns
38760      */   
38761     
38762     maxCols: 0,
38763     
38764     /**
38765      * @cfg {Boolean} isAutoInitial defalut true
38766      */   
38767     isAutoInitial : true, 
38768     
38769     containerWidth: 0,
38770     
38771     /**
38772      * @cfg {Boolean} isHorizontal defalut false
38773      */   
38774     isHorizontal : false, 
38775
38776     currentSize : null,
38777     
38778     tag: 'div',
38779     
38780     cls: '',
38781     
38782     bricks: null, //CompositeElement
38783     
38784     cols : 1,
38785     
38786     _isLayoutInited : false,
38787     
38788 //    isAlternative : false, // only use for vertical layout...
38789     
38790     /**
38791      * @cfg {Number} alternativePadWidth padding below box..
38792      */   
38793     alternativePadWidth : 50,
38794     
38795     selectedBrick : [],
38796     
38797     getAutoCreate : function(){
38798         
38799         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38800         
38801         var cfg = {
38802             tag: this.tag,
38803             cls: 'blog-masonary-wrapper ' + this.cls,
38804             cn : {
38805                 cls : 'mas-boxes masonary'
38806             }
38807         };
38808         
38809         return cfg;
38810     },
38811     
38812     getChildContainer: function( )
38813     {
38814         if (this.boxesEl) {
38815             return this.boxesEl;
38816         }
38817         
38818         this.boxesEl = this.el.select('.mas-boxes').first();
38819         
38820         return this.boxesEl;
38821     },
38822     
38823     
38824     initEvents : function()
38825     {
38826         var _this = this;
38827         
38828         if(this.isAutoInitial){
38829             Roo.log('hook children rendered');
38830             this.on('childrenrendered', function() {
38831                 Roo.log('children rendered');
38832                 _this.initial();
38833             } ,this);
38834         }
38835     },
38836     
38837     initial : function()
38838     {
38839         this.selectedBrick = [];
38840         
38841         this.currentSize = this.el.getBox(true);
38842         
38843         Roo.EventManager.onWindowResize(this.resize, this); 
38844
38845         if(!this.isAutoInitial){
38846             this.layout();
38847             return;
38848         }
38849         
38850         this.layout();
38851         
38852         return;
38853         //this.layout.defer(500,this);
38854         
38855     },
38856     
38857     resize : function()
38858     {
38859         var cs = this.el.getBox(true);
38860         
38861         if (
38862                 this.currentSize.width == cs.width && 
38863                 this.currentSize.x == cs.x && 
38864                 this.currentSize.height == cs.height && 
38865                 this.currentSize.y == cs.y 
38866         ) {
38867             Roo.log("no change in with or X or Y");
38868             return;
38869         }
38870         
38871         this.currentSize = cs;
38872         
38873         this.layout();
38874         
38875     },
38876     
38877     layout : function()
38878     {   
38879         this._resetLayout();
38880         
38881         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38882         
38883         this.layoutItems( isInstant );
38884       
38885         this._isLayoutInited = true;
38886         
38887         this.fireEvent('layout', this);
38888         
38889     },
38890     
38891     _resetLayout : function()
38892     {
38893         if(this.isHorizontal){
38894             this.horizontalMeasureColumns();
38895             return;
38896         }
38897         
38898         this.verticalMeasureColumns();
38899         
38900     },
38901     
38902     verticalMeasureColumns : function()
38903     {
38904         this.getContainerWidth();
38905         
38906 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38907 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
38908 //            return;
38909 //        }
38910         
38911         var boxWidth = this.boxWidth + this.padWidth;
38912         
38913         if(this.containerWidth < this.boxWidth){
38914             boxWidth = this.containerWidth
38915         }
38916         
38917         var containerWidth = this.containerWidth;
38918         
38919         var cols = Math.floor(containerWidth / boxWidth);
38920         
38921         this.cols = Math.max( cols, 1 );
38922         
38923         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38924         
38925         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38926         
38927         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38928         
38929         this.colWidth = boxWidth + avail - this.padWidth;
38930         
38931         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38932         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
38933     },
38934     
38935     horizontalMeasureColumns : function()
38936     {
38937         this.getContainerWidth();
38938         
38939         var boxWidth = this.boxWidth;
38940         
38941         if(this.containerWidth < boxWidth){
38942             boxWidth = this.containerWidth;
38943         }
38944         
38945         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38946         
38947         this.el.setHeight(boxWidth);
38948         
38949     },
38950     
38951     getContainerWidth : function()
38952     {
38953         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38954     },
38955     
38956     layoutItems : function( isInstant )
38957     {
38958         Roo.log(this.bricks);
38959         
38960         var items = Roo.apply([], this.bricks);
38961         
38962         if(this.isHorizontal){
38963             this._horizontalLayoutItems( items , isInstant );
38964             return;
38965         }
38966         
38967 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38968 //            this._verticalAlternativeLayoutItems( items , isInstant );
38969 //            return;
38970 //        }
38971         
38972         this._verticalLayoutItems( items , isInstant );
38973         
38974     },
38975     
38976     _verticalLayoutItems : function ( items , isInstant)
38977     {
38978         if ( !items || !items.length ) {
38979             return;
38980         }
38981         
38982         var standard = [
38983             ['xs', 'xs', 'xs', 'tall'],
38984             ['xs', 'xs', 'tall'],
38985             ['xs', 'xs', 'sm'],
38986             ['xs', 'xs', 'xs'],
38987             ['xs', 'tall'],
38988             ['xs', 'sm'],
38989             ['xs', 'xs'],
38990             ['xs'],
38991             
38992             ['sm', 'xs', 'xs'],
38993             ['sm', 'xs'],
38994             ['sm'],
38995             
38996             ['tall', 'xs', 'xs', 'xs'],
38997             ['tall', 'xs', 'xs'],
38998             ['tall', 'xs'],
38999             ['tall']
39000             
39001         ];
39002         
39003         var queue = [];
39004         
39005         var boxes = [];
39006         
39007         var box = [];
39008         
39009         Roo.each(items, function(item, k){
39010             
39011             switch (item.size) {
39012                 // these layouts take up a full box,
39013                 case 'md' :
39014                 case 'md-left' :
39015                 case 'md-right' :
39016                 case 'wide' :
39017                     
39018                     if(box.length){
39019                         boxes.push(box);
39020                         box = [];
39021                     }
39022                     
39023                     boxes.push([item]);
39024                     
39025                     break;
39026                     
39027                 case 'xs' :
39028                 case 'sm' :
39029                 case 'tall' :
39030                     
39031                     box.push(item);
39032                     
39033                     break;
39034                 default :
39035                     break;
39036                     
39037             }
39038             
39039         }, this);
39040         
39041         if(box.length){
39042             boxes.push(box);
39043             box = [];
39044         }
39045         
39046         var filterPattern = function(box, length)
39047         {
39048             if(!box.length){
39049                 return;
39050             }
39051             
39052             var match = false;
39053             
39054             var pattern = box.slice(0, length);
39055             
39056             var format = [];
39057             
39058             Roo.each(pattern, function(i){
39059                 format.push(i.size);
39060             }, this);
39061             
39062             Roo.each(standard, function(s){
39063                 
39064                 if(String(s) != String(format)){
39065                     return;
39066                 }
39067                 
39068                 match = true;
39069                 return false;
39070                 
39071             }, this);
39072             
39073             if(!match && length == 1){
39074                 return;
39075             }
39076             
39077             if(!match){
39078                 filterPattern(box, length - 1);
39079                 return;
39080             }
39081                 
39082             queue.push(pattern);
39083
39084             box = box.slice(length, box.length);
39085
39086             filterPattern(box, 4);
39087
39088             return;
39089             
39090         }
39091         
39092         Roo.each(boxes, function(box, k){
39093             
39094             if(!box.length){
39095                 return;
39096             }
39097             
39098             if(box.length == 1){
39099                 queue.push(box);
39100                 return;
39101             }
39102             
39103             filterPattern(box, 4);
39104             
39105         }, this);
39106         
39107         this._processVerticalLayoutQueue( queue, isInstant );
39108         
39109     },
39110     
39111 //    _verticalAlternativeLayoutItems : function( items , isInstant )
39112 //    {
39113 //        if ( !items || !items.length ) {
39114 //            return;
39115 //        }
39116 //
39117 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
39118 //        
39119 //    },
39120     
39121     _horizontalLayoutItems : function ( items , isInstant)
39122     {
39123         if ( !items || !items.length || items.length < 3) {
39124             return;
39125         }
39126         
39127         items.reverse();
39128         
39129         var eItems = items.slice(0, 3);
39130         
39131         items = items.slice(3, items.length);
39132         
39133         var standard = [
39134             ['xs', 'xs', 'xs', 'wide'],
39135             ['xs', 'xs', 'wide'],
39136             ['xs', 'xs', 'sm'],
39137             ['xs', 'xs', 'xs'],
39138             ['xs', 'wide'],
39139             ['xs', 'sm'],
39140             ['xs', 'xs'],
39141             ['xs'],
39142             
39143             ['sm', 'xs', 'xs'],
39144             ['sm', 'xs'],
39145             ['sm'],
39146             
39147             ['wide', 'xs', 'xs', 'xs'],
39148             ['wide', 'xs', 'xs'],
39149             ['wide', 'xs'],
39150             ['wide'],
39151             
39152             ['wide-thin']
39153         ];
39154         
39155         var queue = [];
39156         
39157         var boxes = [];
39158         
39159         var box = [];
39160         
39161         Roo.each(items, function(item, k){
39162             
39163             switch (item.size) {
39164                 case 'md' :
39165                 case 'md-left' :
39166                 case 'md-right' :
39167                 case 'tall' :
39168                     
39169                     if(box.length){
39170                         boxes.push(box);
39171                         box = [];
39172                     }
39173                     
39174                     boxes.push([item]);
39175                     
39176                     break;
39177                     
39178                 case 'xs' :
39179                 case 'sm' :
39180                 case 'wide' :
39181                 case 'wide-thin' :
39182                     
39183                     box.push(item);
39184                     
39185                     break;
39186                 default :
39187                     break;
39188                     
39189             }
39190             
39191         }, this);
39192         
39193         if(box.length){
39194             boxes.push(box);
39195             box = [];
39196         }
39197         
39198         var filterPattern = function(box, length)
39199         {
39200             if(!box.length){
39201                 return;
39202             }
39203             
39204             var match = false;
39205             
39206             var pattern = box.slice(0, length);
39207             
39208             var format = [];
39209             
39210             Roo.each(pattern, function(i){
39211                 format.push(i.size);
39212             }, this);
39213             
39214             Roo.each(standard, function(s){
39215                 
39216                 if(String(s) != String(format)){
39217                     return;
39218                 }
39219                 
39220                 match = true;
39221                 return false;
39222                 
39223             }, this);
39224             
39225             if(!match && length == 1){
39226                 return;
39227             }
39228             
39229             if(!match){
39230                 filterPattern(box, length - 1);
39231                 return;
39232             }
39233                 
39234             queue.push(pattern);
39235
39236             box = box.slice(length, box.length);
39237
39238             filterPattern(box, 4);
39239
39240             return;
39241             
39242         }
39243         
39244         Roo.each(boxes, function(box, k){
39245             
39246             if(!box.length){
39247                 return;
39248             }
39249             
39250             if(box.length == 1){
39251                 queue.push(box);
39252                 return;
39253             }
39254             
39255             filterPattern(box, 4);
39256             
39257         }, this);
39258         
39259         
39260         var prune = [];
39261         
39262         var pos = this.el.getBox(true);
39263         
39264         var minX = pos.x;
39265         
39266         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39267         
39268         var hit_end = false;
39269         
39270         Roo.each(queue, function(box){
39271             
39272             if(hit_end){
39273                 
39274                 Roo.each(box, function(b){
39275                 
39276                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39277                     b.el.hide();
39278
39279                 }, this);
39280
39281                 return;
39282             }
39283             
39284             var mx = 0;
39285             
39286             Roo.each(box, function(b){
39287                 
39288                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39289                 b.el.show();
39290
39291                 mx = Math.max(mx, b.x);
39292                 
39293             }, this);
39294             
39295             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39296             
39297             if(maxX < minX){
39298                 
39299                 Roo.each(box, function(b){
39300                 
39301                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39302                     b.el.hide();
39303                     
39304                 }, this);
39305                 
39306                 hit_end = true;
39307                 
39308                 return;
39309             }
39310             
39311             prune.push(box);
39312             
39313         }, this);
39314         
39315         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39316     },
39317     
39318     /** Sets position of item in DOM
39319     * @param {Element} item
39320     * @param {Number} x - horizontal position
39321     * @param {Number} y - vertical position
39322     * @param {Boolean} isInstant - disables transitions
39323     */
39324     _processVerticalLayoutQueue : function( queue, isInstant )
39325     {
39326         var pos = this.el.getBox(true);
39327         var x = pos.x;
39328         var y = pos.y;
39329         var maxY = [];
39330         
39331         for (var i = 0; i < this.cols; i++){
39332             maxY[i] = pos.y;
39333         }
39334         
39335         Roo.each(queue, function(box, k){
39336             
39337             var col = k % this.cols;
39338             
39339             Roo.each(box, function(b,kk){
39340                 
39341                 b.el.position('absolute');
39342                 
39343                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39344                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39345                 
39346                 if(b.size == 'md-left' || b.size == 'md-right'){
39347                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39348                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39349                 }
39350                 
39351                 b.el.setWidth(width);
39352                 b.el.setHeight(height);
39353                 // iframe?
39354                 b.el.select('iframe',true).setSize(width,height);
39355                 
39356             }, this);
39357             
39358             for (var i = 0; i < this.cols; i++){
39359                 
39360                 if(maxY[i] < maxY[col]){
39361                     col = i;
39362                     continue;
39363                 }
39364                 
39365                 col = Math.min(col, i);
39366                 
39367             }
39368             
39369             x = pos.x + col * (this.colWidth + this.padWidth);
39370             
39371             y = maxY[col];
39372             
39373             var positions = [];
39374             
39375             switch (box.length){
39376                 case 1 :
39377                     positions = this.getVerticalOneBoxColPositions(x, y, box);
39378                     break;
39379                 case 2 :
39380                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
39381                     break;
39382                 case 3 :
39383                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
39384                     break;
39385                 case 4 :
39386                     positions = this.getVerticalFourBoxColPositions(x, y, box);
39387                     break;
39388                 default :
39389                     break;
39390             }
39391             
39392             Roo.each(box, function(b,kk){
39393                 
39394                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39395                 
39396                 var sz = b.el.getSize();
39397                 
39398                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39399                 
39400             }, this);
39401             
39402         }, this);
39403         
39404         var mY = 0;
39405         
39406         for (var i = 0; i < this.cols; i++){
39407             mY = Math.max(mY, maxY[i]);
39408         }
39409         
39410         this.el.setHeight(mY - pos.y);
39411         
39412     },
39413     
39414 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39415 //    {
39416 //        var pos = this.el.getBox(true);
39417 //        var x = pos.x;
39418 //        var y = pos.y;
39419 //        var maxX = pos.right;
39420 //        
39421 //        var maxHeight = 0;
39422 //        
39423 //        Roo.each(items, function(item, k){
39424 //            
39425 //            var c = k % 2;
39426 //            
39427 //            item.el.position('absolute');
39428 //                
39429 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39430 //
39431 //            item.el.setWidth(width);
39432 //
39433 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39434 //
39435 //            item.el.setHeight(height);
39436 //            
39437 //            if(c == 0){
39438 //                item.el.setXY([x, y], isInstant ? false : true);
39439 //            } else {
39440 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
39441 //            }
39442 //            
39443 //            y = y + height + this.alternativePadWidth;
39444 //            
39445 //            maxHeight = maxHeight + height + this.alternativePadWidth;
39446 //            
39447 //        }, this);
39448 //        
39449 //        this.el.setHeight(maxHeight);
39450 //        
39451 //    },
39452     
39453     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39454     {
39455         var pos = this.el.getBox(true);
39456         
39457         var minX = pos.x;
39458         var minY = pos.y;
39459         
39460         var maxX = pos.right;
39461         
39462         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39463         
39464         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39465         
39466         Roo.each(queue, function(box, k){
39467             
39468             Roo.each(box, function(b, kk){
39469                 
39470                 b.el.position('absolute');
39471                 
39472                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39473                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39474                 
39475                 if(b.size == 'md-left' || b.size == 'md-right'){
39476                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39477                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39478                 }
39479                 
39480                 b.el.setWidth(width);
39481                 b.el.setHeight(height);
39482                 
39483             }, this);
39484             
39485             if(!box.length){
39486                 return;
39487             }
39488             
39489             var positions = [];
39490             
39491             switch (box.length){
39492                 case 1 :
39493                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39494                     break;
39495                 case 2 :
39496                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39497                     break;
39498                 case 3 :
39499                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39500                     break;
39501                 case 4 :
39502                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39503                     break;
39504                 default :
39505                     break;
39506             }
39507             
39508             Roo.each(box, function(b,kk){
39509                 
39510                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39511                 
39512                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39513                 
39514             }, this);
39515             
39516         }, this);
39517         
39518     },
39519     
39520     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39521     {
39522         Roo.each(eItems, function(b,k){
39523             
39524             b.size = (k == 0) ? 'sm' : 'xs';
39525             b.x = (k == 0) ? 2 : 1;
39526             b.y = (k == 0) ? 2 : 1;
39527             
39528             b.el.position('absolute');
39529             
39530             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39531                 
39532             b.el.setWidth(width);
39533             
39534             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39535             
39536             b.el.setHeight(height);
39537             
39538         }, this);
39539
39540         var positions = [];
39541         
39542         positions.push({
39543             x : maxX - this.unitWidth * 2 - this.gutter,
39544             y : minY
39545         });
39546         
39547         positions.push({
39548             x : maxX - this.unitWidth,
39549             y : minY + (this.unitWidth + this.gutter) * 2
39550         });
39551         
39552         positions.push({
39553             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39554             y : minY
39555         });
39556         
39557         Roo.each(eItems, function(b,k){
39558             
39559             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39560
39561         }, this);
39562         
39563     },
39564     
39565     getVerticalOneBoxColPositions : function(x, y, box)
39566     {
39567         var pos = [];
39568         
39569         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39570         
39571         if(box[0].size == 'md-left'){
39572             rand = 0;
39573         }
39574         
39575         if(box[0].size == 'md-right'){
39576             rand = 1;
39577         }
39578         
39579         pos.push({
39580             x : x + (this.unitWidth + this.gutter) * rand,
39581             y : y
39582         });
39583         
39584         return pos;
39585     },
39586     
39587     getVerticalTwoBoxColPositions : function(x, y, box)
39588     {
39589         var pos = [];
39590         
39591         if(box[0].size == 'xs'){
39592             
39593             pos.push({
39594                 x : x,
39595                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39596             });
39597
39598             pos.push({
39599                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39600                 y : y
39601             });
39602             
39603             return pos;
39604             
39605         }
39606         
39607         pos.push({
39608             x : x,
39609             y : y
39610         });
39611
39612         pos.push({
39613             x : x + (this.unitWidth + this.gutter) * 2,
39614             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39615         });
39616         
39617         return pos;
39618         
39619     },
39620     
39621     getVerticalThreeBoxColPositions : function(x, y, box)
39622     {
39623         var pos = [];
39624         
39625         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39626             
39627             pos.push({
39628                 x : x,
39629                 y : y
39630             });
39631
39632             pos.push({
39633                 x : x + (this.unitWidth + this.gutter) * 1,
39634                 y : y
39635             });
39636             
39637             pos.push({
39638                 x : x + (this.unitWidth + this.gutter) * 2,
39639                 y : y
39640             });
39641             
39642             return pos;
39643             
39644         }
39645         
39646         if(box[0].size == 'xs' && box[1].size == 'xs'){
39647             
39648             pos.push({
39649                 x : x,
39650                 y : y
39651             });
39652
39653             pos.push({
39654                 x : x,
39655                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39656             });
39657             
39658             pos.push({
39659                 x : x + (this.unitWidth + this.gutter) * 1,
39660                 y : y
39661             });
39662             
39663             return pos;
39664             
39665         }
39666         
39667         pos.push({
39668             x : x,
39669             y : y
39670         });
39671
39672         pos.push({
39673             x : x + (this.unitWidth + this.gutter) * 2,
39674             y : y
39675         });
39676
39677         pos.push({
39678             x : x + (this.unitWidth + this.gutter) * 2,
39679             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39680         });
39681             
39682         return pos;
39683         
39684     },
39685     
39686     getVerticalFourBoxColPositions : function(x, y, box)
39687     {
39688         var pos = [];
39689         
39690         if(box[0].size == 'xs'){
39691             
39692             pos.push({
39693                 x : x,
39694                 y : y
39695             });
39696
39697             pos.push({
39698                 x : x,
39699                 y : y + (this.unitHeight + this.gutter) * 1
39700             });
39701             
39702             pos.push({
39703                 x : x,
39704                 y : y + (this.unitHeight + this.gutter) * 2
39705             });
39706             
39707             pos.push({
39708                 x : x + (this.unitWidth + this.gutter) * 1,
39709                 y : y
39710             });
39711             
39712             return pos;
39713             
39714         }
39715         
39716         pos.push({
39717             x : x,
39718             y : y
39719         });
39720
39721         pos.push({
39722             x : x + (this.unitWidth + this.gutter) * 2,
39723             y : y
39724         });
39725
39726         pos.push({
39727             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39728             y : y + (this.unitHeight + this.gutter) * 1
39729         });
39730
39731         pos.push({
39732             x : x + (this.unitWidth + this.gutter) * 2,
39733             y : y + (this.unitWidth + this.gutter) * 2
39734         });
39735
39736         return pos;
39737         
39738     },
39739     
39740     getHorizontalOneBoxColPositions : function(maxX, minY, box)
39741     {
39742         var pos = [];
39743         
39744         if(box[0].size == 'md-left'){
39745             pos.push({
39746                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39747                 y : minY
39748             });
39749             
39750             return pos;
39751         }
39752         
39753         if(box[0].size == 'md-right'){
39754             pos.push({
39755                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39756                 y : minY + (this.unitWidth + this.gutter) * 1
39757             });
39758             
39759             return pos;
39760         }
39761         
39762         var rand = Math.floor(Math.random() * (4 - box[0].y));
39763         
39764         pos.push({
39765             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39766             y : minY + (this.unitWidth + this.gutter) * rand
39767         });
39768         
39769         return pos;
39770         
39771     },
39772     
39773     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39774     {
39775         var pos = [];
39776         
39777         if(box[0].size == 'xs'){
39778             
39779             pos.push({
39780                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39781                 y : minY
39782             });
39783
39784             pos.push({
39785                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39786                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39787             });
39788             
39789             return pos;
39790             
39791         }
39792         
39793         pos.push({
39794             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39795             y : minY
39796         });
39797
39798         pos.push({
39799             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39800             y : minY + (this.unitWidth + this.gutter) * 2
39801         });
39802         
39803         return pos;
39804         
39805     },
39806     
39807     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39808     {
39809         var pos = [];
39810         
39811         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39812             
39813             pos.push({
39814                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39815                 y : minY
39816             });
39817
39818             pos.push({
39819                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39820                 y : minY + (this.unitWidth + this.gutter) * 1
39821             });
39822             
39823             pos.push({
39824                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39825                 y : minY + (this.unitWidth + this.gutter) * 2
39826             });
39827             
39828             return pos;
39829             
39830         }
39831         
39832         if(box[0].size == 'xs' && box[1].size == 'xs'){
39833             
39834             pos.push({
39835                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39836                 y : minY
39837             });
39838
39839             pos.push({
39840                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39841                 y : minY
39842             });
39843             
39844             pos.push({
39845                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39846                 y : minY + (this.unitWidth + this.gutter) * 1
39847             });
39848             
39849             return pos;
39850             
39851         }
39852         
39853         pos.push({
39854             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39855             y : minY
39856         });
39857
39858         pos.push({
39859             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39860             y : minY + (this.unitWidth + this.gutter) * 2
39861         });
39862
39863         pos.push({
39864             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39865             y : minY + (this.unitWidth + this.gutter) * 2
39866         });
39867             
39868         return pos;
39869         
39870     },
39871     
39872     getHorizontalFourBoxColPositions : function(maxX, minY, box)
39873     {
39874         var pos = [];
39875         
39876         if(box[0].size == 'xs'){
39877             
39878             pos.push({
39879                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39880                 y : minY
39881             });
39882
39883             pos.push({
39884                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39885                 y : minY
39886             });
39887             
39888             pos.push({
39889                 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),
39890                 y : minY
39891             });
39892             
39893             pos.push({
39894                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39895                 y : minY + (this.unitWidth + this.gutter) * 1
39896             });
39897             
39898             return pos;
39899             
39900         }
39901         
39902         pos.push({
39903             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39904             y : minY
39905         });
39906         
39907         pos.push({
39908             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39909             y : minY + (this.unitWidth + this.gutter) * 2
39910         });
39911         
39912         pos.push({
39913             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39914             y : minY + (this.unitWidth + this.gutter) * 2
39915         });
39916         
39917         pos.push({
39918             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),
39919             y : minY + (this.unitWidth + this.gutter) * 2
39920         });
39921
39922         return pos;
39923         
39924     },
39925     
39926     /**
39927     * remove a Masonry Brick
39928     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39929     */
39930     removeBrick : function(brick_id)
39931     {
39932         if (!brick_id) {
39933             return;
39934         }
39935         
39936         for (var i = 0; i<this.bricks.length; i++) {
39937             if (this.bricks[i].id == brick_id) {
39938                 this.bricks.splice(i,1);
39939                 this.el.dom.removeChild(Roo.get(brick_id).dom);
39940                 this.initial();
39941             }
39942         }
39943     },
39944     
39945     /**
39946     * adds a Masonry Brick
39947     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39948     */
39949     addBrick : function(cfg)
39950     {
39951         var cn = new Roo.bootstrap.MasonryBrick(cfg);
39952         //this.register(cn);
39953         cn.parentId = this.id;
39954         cn.render(this.el);
39955         return cn;
39956     },
39957     
39958     /**
39959     * register a Masonry Brick
39960     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39961     */
39962     
39963     register : function(brick)
39964     {
39965         this.bricks.push(brick);
39966         brick.masonryId = this.id;
39967     },
39968     
39969     /**
39970     * clear all the Masonry Brick
39971     */
39972     clearAll : function()
39973     {
39974         this.bricks = [];
39975         //this.getChildContainer().dom.innerHTML = "";
39976         this.el.dom.innerHTML = '';
39977     },
39978     
39979     getSelected : function()
39980     {
39981         if (!this.selectedBrick) {
39982             return false;
39983         }
39984         
39985         return this.selectedBrick;
39986     }
39987 });
39988
39989 Roo.apply(Roo.bootstrap.LayoutMasonry, {
39990     
39991     groups: {},
39992      /**
39993     * register a Masonry Layout
39994     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
39995     */
39996     
39997     register : function(layout)
39998     {
39999         this.groups[layout.id] = layout;
40000     },
40001     /**
40002     * fetch a  Masonry Layout based on the masonry layout ID
40003     * @param {string} the masonry layout to add
40004     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40005     */
40006     
40007     get: function(layout_id) {
40008         if (typeof(this.groups[layout_id]) == 'undefined') {
40009             return false;
40010         }
40011         return this.groups[layout_id] ;
40012     }
40013     
40014     
40015     
40016 });
40017
40018  
40019
40020  /**
40021  *
40022  * This is based on 
40023  * http://masonry.desandro.com
40024  *
40025  * The idea is to render all the bricks based on vertical width...
40026  *
40027  * The original code extends 'outlayer' - we might need to use that....
40028  * 
40029  */
40030
40031
40032 /**
40033  * @class Roo.bootstrap.LayoutMasonryAuto
40034  * @extends Roo.bootstrap.Component
40035  * Bootstrap Layout Masonry class
40036  * 
40037  * @constructor
40038  * Create a new Element
40039  * @param {Object} config The config object
40040  */
40041
40042 Roo.bootstrap.LayoutMasonryAuto = function(config){
40043     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40044 };
40045
40046 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
40047     
40048       /**
40049      * @cfg {Boolean} isFitWidth  - resize the width..
40050      */   
40051     isFitWidth : false,  // options..
40052     /**
40053      * @cfg {Boolean} isOriginLeft = left align?
40054      */   
40055     isOriginLeft : true,
40056     /**
40057      * @cfg {Boolean} isOriginTop = top align?
40058      */   
40059     isOriginTop : false,
40060     /**
40061      * @cfg {Boolean} isLayoutInstant = no animation?
40062      */   
40063     isLayoutInstant : false, // needed?
40064     /**
40065      * @cfg {Boolean} isResizingContainer = not sure if this is used..
40066      */   
40067     isResizingContainer : true,
40068     /**
40069      * @cfg {Number} columnWidth  width of the columns 
40070      */   
40071     
40072     columnWidth : 0,
40073     
40074     /**
40075      * @cfg {Number} maxCols maximum number of columns
40076      */   
40077     
40078     maxCols: 0,
40079     /**
40080      * @cfg {Number} padHeight padding below box..
40081      */   
40082     
40083     padHeight : 10, 
40084     
40085     /**
40086      * @cfg {Boolean} isAutoInitial defalut true
40087      */   
40088     
40089     isAutoInitial : true, 
40090     
40091     // private?
40092     gutter : 0,
40093     
40094     containerWidth: 0,
40095     initialColumnWidth : 0,
40096     currentSize : null,
40097     
40098     colYs : null, // array.
40099     maxY : 0,
40100     padWidth: 10,
40101     
40102     
40103     tag: 'div',
40104     cls: '',
40105     bricks: null, //CompositeElement
40106     cols : 0, // array?
40107     // element : null, // wrapped now this.el
40108     _isLayoutInited : null, 
40109     
40110     
40111     getAutoCreate : function(){
40112         
40113         var cfg = {
40114             tag: this.tag,
40115             cls: 'blog-masonary-wrapper ' + this.cls,
40116             cn : {
40117                 cls : 'mas-boxes masonary'
40118             }
40119         };
40120         
40121         return cfg;
40122     },
40123     
40124     getChildContainer: function( )
40125     {
40126         if (this.boxesEl) {
40127             return this.boxesEl;
40128         }
40129         
40130         this.boxesEl = this.el.select('.mas-boxes').first();
40131         
40132         return this.boxesEl;
40133     },
40134     
40135     
40136     initEvents : function()
40137     {
40138         var _this = this;
40139         
40140         if(this.isAutoInitial){
40141             Roo.log('hook children rendered');
40142             this.on('childrenrendered', function() {
40143                 Roo.log('children rendered');
40144                 _this.initial();
40145             } ,this);
40146         }
40147         
40148     },
40149     
40150     initial : function()
40151     {
40152         this.reloadItems();
40153
40154         this.currentSize = this.el.getBox(true);
40155
40156         /// was window resize... - let's see if this works..
40157         Roo.EventManager.onWindowResize(this.resize, this); 
40158
40159         if(!this.isAutoInitial){
40160             this.layout();
40161             return;
40162         }
40163         
40164         this.layout.defer(500,this);
40165     },
40166     
40167     reloadItems: function()
40168     {
40169         this.bricks = this.el.select('.masonry-brick', true);
40170         
40171         this.bricks.each(function(b) {
40172             //Roo.log(b.getSize());
40173             if (!b.attr('originalwidth')) {
40174                 b.attr('originalwidth',  b.getSize().width);
40175             }
40176             
40177         });
40178         
40179         Roo.log(this.bricks.elements.length);
40180     },
40181     
40182     resize : function()
40183     {
40184         Roo.log('resize');
40185         var cs = this.el.getBox(true);
40186         
40187         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40188             Roo.log("no change in with or X");
40189             return;
40190         }
40191         this.currentSize = cs;
40192         this.layout();
40193     },
40194     
40195     layout : function()
40196     {
40197          Roo.log('layout');
40198         this._resetLayout();
40199         //this._manageStamps();
40200       
40201         // don't animate first layout
40202         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40203         this.layoutItems( isInstant );
40204       
40205         // flag for initalized
40206         this._isLayoutInited = true;
40207     },
40208     
40209     layoutItems : function( isInstant )
40210     {
40211         //var items = this._getItemsForLayout( this.items );
40212         // original code supports filtering layout items.. we just ignore it..
40213         
40214         this._layoutItems( this.bricks , isInstant );
40215       
40216         this._postLayout();
40217     },
40218     _layoutItems : function ( items , isInstant)
40219     {
40220        //this.fireEvent( 'layout', this, items );
40221     
40222
40223         if ( !items || !items.elements.length ) {
40224           // no items, emit event with empty array
40225             return;
40226         }
40227
40228         var queue = [];
40229         items.each(function(item) {
40230             Roo.log("layout item");
40231             Roo.log(item);
40232             // get x/y object from method
40233             var position = this._getItemLayoutPosition( item );
40234             // enqueue
40235             position.item = item;
40236             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40237             queue.push( position );
40238         }, this);
40239       
40240         this._processLayoutQueue( queue );
40241     },
40242     /** Sets position of item in DOM
40243     * @param {Element} item
40244     * @param {Number} x - horizontal position
40245     * @param {Number} y - vertical position
40246     * @param {Boolean} isInstant - disables transitions
40247     */
40248     _processLayoutQueue : function( queue )
40249     {
40250         for ( var i=0, len = queue.length; i < len; i++ ) {
40251             var obj = queue[i];
40252             obj.item.position('absolute');
40253             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40254         }
40255     },
40256       
40257     
40258     /**
40259     * Any logic you want to do after each layout,
40260     * i.e. size the container
40261     */
40262     _postLayout : function()
40263     {
40264         this.resizeContainer();
40265     },
40266     
40267     resizeContainer : function()
40268     {
40269         if ( !this.isResizingContainer ) {
40270             return;
40271         }
40272         var size = this._getContainerSize();
40273         if ( size ) {
40274             this.el.setSize(size.width,size.height);
40275             this.boxesEl.setSize(size.width,size.height);
40276         }
40277     },
40278     
40279     
40280     
40281     _resetLayout : function()
40282     {
40283         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40284         this.colWidth = this.el.getWidth();
40285         //this.gutter = this.el.getWidth(); 
40286         
40287         this.measureColumns();
40288
40289         // reset column Y
40290         var i = this.cols;
40291         this.colYs = [];
40292         while (i--) {
40293             this.colYs.push( 0 );
40294         }
40295     
40296         this.maxY = 0;
40297     },
40298
40299     measureColumns : function()
40300     {
40301         this.getContainerWidth();
40302       // if columnWidth is 0, default to outerWidth of first item
40303         if ( !this.columnWidth ) {
40304             var firstItem = this.bricks.first();
40305             Roo.log(firstItem);
40306             this.columnWidth  = this.containerWidth;
40307             if (firstItem && firstItem.attr('originalwidth') ) {
40308                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40309             }
40310             // columnWidth fall back to item of first element
40311             Roo.log("set column width?");
40312                         this.initialColumnWidth = this.columnWidth  ;
40313
40314             // if first elem has no width, default to size of container
40315             
40316         }
40317         
40318         
40319         if (this.initialColumnWidth) {
40320             this.columnWidth = this.initialColumnWidth;
40321         }
40322         
40323         
40324             
40325         // column width is fixed at the top - however if container width get's smaller we should
40326         // reduce it...
40327         
40328         // this bit calcs how man columns..
40329             
40330         var columnWidth = this.columnWidth += this.gutter;
40331       
40332         // calculate columns
40333         var containerWidth = this.containerWidth + this.gutter;
40334         
40335         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40336         // fix rounding errors, typically with gutters
40337         var excess = columnWidth - containerWidth % columnWidth;
40338         
40339         
40340         // if overshoot is less than a pixel, round up, otherwise floor it
40341         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40342         cols = Math[ mathMethod ]( cols );
40343         this.cols = Math.max( cols, 1 );
40344         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40345         
40346          // padding positioning..
40347         var totalColWidth = this.cols * this.columnWidth;
40348         var padavail = this.containerWidth - totalColWidth;
40349         // so for 2 columns - we need 3 'pads'
40350         
40351         var padNeeded = (1+this.cols) * this.padWidth;
40352         
40353         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40354         
40355         this.columnWidth += padExtra
40356         //this.padWidth = Math.floor(padavail /  ( this.cols));
40357         
40358         // adjust colum width so that padding is fixed??
40359         
40360         // we have 3 columns ... total = width * 3
40361         // we have X left over... that should be used by 
40362         
40363         //if (this.expandC) {
40364             
40365         //}
40366         
40367         
40368         
40369     },
40370     
40371     getContainerWidth : function()
40372     {
40373        /* // container is parent if fit width
40374         var container = this.isFitWidth ? this.element.parentNode : this.element;
40375         // check that this.size and size are there
40376         // IE8 triggers resize on body size change, so they might not be
40377         
40378         var size = getSize( container );  //FIXME
40379         this.containerWidth = size && size.innerWidth; //FIXME
40380         */
40381          
40382         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
40383         
40384     },
40385     
40386     _getItemLayoutPosition : function( item )  // what is item?
40387     {
40388         // we resize the item to our columnWidth..
40389       
40390         item.setWidth(this.columnWidth);
40391         item.autoBoxAdjust  = false;
40392         
40393         var sz = item.getSize();
40394  
40395         // how many columns does this brick span
40396         var remainder = this.containerWidth % this.columnWidth;
40397         
40398         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40399         // round if off by 1 pixel, otherwise use ceil
40400         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
40401         colSpan = Math.min( colSpan, this.cols );
40402         
40403         // normally this should be '1' as we dont' currently allow multi width columns..
40404         
40405         var colGroup = this._getColGroup( colSpan );
40406         // get the minimum Y value from the columns
40407         var minimumY = Math.min.apply( Math, colGroup );
40408         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40409         
40410         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
40411          
40412         // position the brick
40413         var position = {
40414             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40415             y: this.currentSize.y + minimumY + this.padHeight
40416         };
40417         
40418         Roo.log(position);
40419         // apply setHeight to necessary columns
40420         var setHeight = minimumY + sz.height + this.padHeight;
40421         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40422         
40423         var setSpan = this.cols + 1 - colGroup.length;
40424         for ( var i = 0; i < setSpan; i++ ) {
40425           this.colYs[ shortColIndex + i ] = setHeight ;
40426         }
40427       
40428         return position;
40429     },
40430     
40431     /**
40432      * @param {Number} colSpan - number of columns the element spans
40433      * @returns {Array} colGroup
40434      */
40435     _getColGroup : function( colSpan )
40436     {
40437         if ( colSpan < 2 ) {
40438           // if brick spans only one column, use all the column Ys
40439           return this.colYs;
40440         }
40441       
40442         var colGroup = [];
40443         // how many different places could this brick fit horizontally
40444         var groupCount = this.cols + 1 - colSpan;
40445         // for each group potential horizontal position
40446         for ( var i = 0; i < groupCount; i++ ) {
40447           // make an array of colY values for that one group
40448           var groupColYs = this.colYs.slice( i, i + colSpan );
40449           // and get the max value of the array
40450           colGroup[i] = Math.max.apply( Math, groupColYs );
40451         }
40452         return colGroup;
40453     },
40454     /*
40455     _manageStamp : function( stamp )
40456     {
40457         var stampSize =  stamp.getSize();
40458         var offset = stamp.getBox();
40459         // get the columns that this stamp affects
40460         var firstX = this.isOriginLeft ? offset.x : offset.right;
40461         var lastX = firstX + stampSize.width;
40462         var firstCol = Math.floor( firstX / this.columnWidth );
40463         firstCol = Math.max( 0, firstCol );
40464         
40465         var lastCol = Math.floor( lastX / this.columnWidth );
40466         // lastCol should not go over if multiple of columnWidth #425
40467         lastCol -= lastX % this.columnWidth ? 0 : 1;
40468         lastCol = Math.min( this.cols - 1, lastCol );
40469         
40470         // set colYs to bottom of the stamp
40471         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40472             stampSize.height;
40473             
40474         for ( var i = firstCol; i <= lastCol; i++ ) {
40475           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40476         }
40477     },
40478     */
40479     
40480     _getContainerSize : function()
40481     {
40482         this.maxY = Math.max.apply( Math, this.colYs );
40483         var size = {
40484             height: this.maxY
40485         };
40486       
40487         if ( this.isFitWidth ) {
40488             size.width = this._getContainerFitWidth();
40489         }
40490       
40491         return size;
40492     },
40493     
40494     _getContainerFitWidth : function()
40495     {
40496         var unusedCols = 0;
40497         // count unused columns
40498         var i = this.cols;
40499         while ( --i ) {
40500           if ( this.colYs[i] !== 0 ) {
40501             break;
40502           }
40503           unusedCols++;
40504         }
40505         // fit container to columns that have been used
40506         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40507     },
40508     
40509     needsResizeLayout : function()
40510     {
40511         var previousWidth = this.containerWidth;
40512         this.getContainerWidth();
40513         return previousWidth !== this.containerWidth;
40514     }
40515  
40516 });
40517
40518  
40519
40520  /*
40521  * - LGPL
40522  *
40523  * element
40524  * 
40525  */
40526
40527 /**
40528  * @class Roo.bootstrap.MasonryBrick
40529  * @extends Roo.bootstrap.Component
40530  * Bootstrap MasonryBrick class
40531  * 
40532  * @constructor
40533  * Create a new MasonryBrick
40534  * @param {Object} config The config object
40535  */
40536
40537 Roo.bootstrap.MasonryBrick = function(config){
40538     
40539     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40540     
40541     Roo.bootstrap.MasonryBrick.register(this);
40542     
40543     this.addEvents({
40544         // raw events
40545         /**
40546          * @event click
40547          * When a MasonryBrick is clcik
40548          * @param {Roo.bootstrap.MasonryBrick} this
40549          * @param {Roo.EventObject} e
40550          */
40551         "click" : true
40552     });
40553 };
40554
40555 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40556     
40557     /**
40558      * @cfg {String} title
40559      */   
40560     title : '',
40561     /**
40562      * @cfg {String} html
40563      */   
40564     html : '',
40565     /**
40566      * @cfg {String} bgimage
40567      */   
40568     bgimage : '',
40569     /**
40570      * @cfg {String} videourl
40571      */   
40572     videourl : '',
40573     /**
40574      * @cfg {String} cls
40575      */   
40576     cls : '',
40577     /**
40578      * @cfg {String} href
40579      */   
40580     href : '',
40581     /**
40582      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40583      */   
40584     size : 'xs',
40585     
40586     /**
40587      * @cfg {String} placetitle (center|bottom)
40588      */   
40589     placetitle : '',
40590     
40591     /**
40592      * @cfg {Boolean} isFitContainer defalut true
40593      */   
40594     isFitContainer : true, 
40595     
40596     /**
40597      * @cfg {Boolean} preventDefault defalut false
40598      */   
40599     preventDefault : false, 
40600     
40601     /**
40602      * @cfg {Boolean} inverse defalut false
40603      */   
40604     maskInverse : false, 
40605     
40606     getAutoCreate : function()
40607     {
40608         if(!this.isFitContainer){
40609             return this.getSplitAutoCreate();
40610         }
40611         
40612         var cls = 'masonry-brick masonry-brick-full';
40613         
40614         if(this.href.length){
40615             cls += ' masonry-brick-link';
40616         }
40617         
40618         if(this.bgimage.length){
40619             cls += ' masonry-brick-image';
40620         }
40621         
40622         if(this.maskInverse){
40623             cls += ' mask-inverse';
40624         }
40625         
40626         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40627             cls += ' enable-mask';
40628         }
40629         
40630         if(this.size){
40631             cls += ' masonry-' + this.size + '-brick';
40632         }
40633         
40634         if(this.placetitle.length){
40635             
40636             switch (this.placetitle) {
40637                 case 'center' :
40638                     cls += ' masonry-center-title';
40639                     break;
40640                 case 'bottom' :
40641                     cls += ' masonry-bottom-title';
40642                     break;
40643                 default:
40644                     break;
40645             }
40646             
40647         } else {
40648             if(!this.html.length && !this.bgimage.length){
40649                 cls += ' masonry-center-title';
40650             }
40651
40652             if(!this.html.length && this.bgimage.length){
40653                 cls += ' masonry-bottom-title';
40654             }
40655         }
40656         
40657         if(this.cls){
40658             cls += ' ' + this.cls;
40659         }
40660         
40661         var cfg = {
40662             tag: (this.href.length) ? 'a' : 'div',
40663             cls: cls,
40664             cn: [
40665                 {
40666                     tag: 'div',
40667                     cls: 'masonry-brick-mask'
40668                 },
40669                 {
40670                     tag: 'div',
40671                     cls: 'masonry-brick-paragraph',
40672                     cn: []
40673                 }
40674             ]
40675         };
40676         
40677         if(this.href.length){
40678             cfg.href = this.href;
40679         }
40680         
40681         var cn = cfg.cn[1].cn;
40682         
40683         if(this.title.length){
40684             cn.push({
40685                 tag: 'h4',
40686                 cls: 'masonry-brick-title',
40687                 html: this.title
40688             });
40689         }
40690         
40691         if(this.html.length){
40692             cn.push({
40693                 tag: 'p',
40694                 cls: 'masonry-brick-text',
40695                 html: this.html
40696             });
40697         }
40698         
40699         if (!this.title.length && !this.html.length) {
40700             cfg.cn[1].cls += ' hide';
40701         }
40702         
40703         if(this.bgimage.length){
40704             cfg.cn.push({
40705                 tag: 'img',
40706                 cls: 'masonry-brick-image-view',
40707                 src: this.bgimage
40708             });
40709         }
40710         
40711         if(this.videourl.length){
40712             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40713             // youtube support only?
40714             cfg.cn.push({
40715                 tag: 'iframe',
40716                 cls: 'masonry-brick-image-view',
40717                 src: vurl,
40718                 frameborder : 0,
40719                 allowfullscreen : true
40720             });
40721         }
40722         
40723         return cfg;
40724         
40725     },
40726     
40727     getSplitAutoCreate : function()
40728     {
40729         var cls = 'masonry-brick masonry-brick-split';
40730         
40731         if(this.href.length){
40732             cls += ' masonry-brick-link';
40733         }
40734         
40735         if(this.bgimage.length){
40736             cls += ' masonry-brick-image';
40737         }
40738         
40739         if(this.size){
40740             cls += ' masonry-' + this.size + '-brick';
40741         }
40742         
40743         switch (this.placetitle) {
40744             case 'center' :
40745                 cls += ' masonry-center-title';
40746                 break;
40747             case 'bottom' :
40748                 cls += ' masonry-bottom-title';
40749                 break;
40750             default:
40751                 if(!this.bgimage.length){
40752                     cls += ' masonry-center-title';
40753                 }
40754
40755                 if(this.bgimage.length){
40756                     cls += ' masonry-bottom-title';
40757                 }
40758                 break;
40759         }
40760         
40761         if(this.cls){
40762             cls += ' ' + this.cls;
40763         }
40764         
40765         var cfg = {
40766             tag: (this.href.length) ? 'a' : 'div',
40767             cls: cls,
40768             cn: [
40769                 {
40770                     tag: 'div',
40771                     cls: 'masonry-brick-split-head',
40772                     cn: [
40773                         {
40774                             tag: 'div',
40775                             cls: 'masonry-brick-paragraph',
40776                             cn: []
40777                         }
40778                     ]
40779                 },
40780                 {
40781                     tag: 'div',
40782                     cls: 'masonry-brick-split-body',
40783                     cn: []
40784                 }
40785             ]
40786         };
40787         
40788         if(this.href.length){
40789             cfg.href = this.href;
40790         }
40791         
40792         if(this.title.length){
40793             cfg.cn[0].cn[0].cn.push({
40794                 tag: 'h4',
40795                 cls: 'masonry-brick-title',
40796                 html: this.title
40797             });
40798         }
40799         
40800         if(this.html.length){
40801             cfg.cn[1].cn.push({
40802                 tag: 'p',
40803                 cls: 'masonry-brick-text',
40804                 html: this.html
40805             });
40806         }
40807
40808         if(this.bgimage.length){
40809             cfg.cn[0].cn.push({
40810                 tag: 'img',
40811                 cls: 'masonry-brick-image-view',
40812                 src: this.bgimage
40813             });
40814         }
40815         
40816         if(this.videourl.length){
40817             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40818             // youtube support only?
40819             cfg.cn[0].cn.cn.push({
40820                 tag: 'iframe',
40821                 cls: 'masonry-brick-image-view',
40822                 src: vurl,
40823                 frameborder : 0,
40824                 allowfullscreen : true
40825             });
40826         }
40827         
40828         return cfg;
40829     },
40830     
40831     initEvents: function() 
40832     {
40833         switch (this.size) {
40834             case 'xs' :
40835                 this.x = 1;
40836                 this.y = 1;
40837                 break;
40838             case 'sm' :
40839                 this.x = 2;
40840                 this.y = 2;
40841                 break;
40842             case 'md' :
40843             case 'md-left' :
40844             case 'md-right' :
40845                 this.x = 3;
40846                 this.y = 3;
40847                 break;
40848             case 'tall' :
40849                 this.x = 2;
40850                 this.y = 3;
40851                 break;
40852             case 'wide' :
40853                 this.x = 3;
40854                 this.y = 2;
40855                 break;
40856             case 'wide-thin' :
40857                 this.x = 3;
40858                 this.y = 1;
40859                 break;
40860                         
40861             default :
40862                 break;
40863         }
40864         
40865         if(Roo.isTouch){
40866             this.el.on('touchstart', this.onTouchStart, this);
40867             this.el.on('touchmove', this.onTouchMove, this);
40868             this.el.on('touchend', this.onTouchEnd, this);
40869             this.el.on('contextmenu', this.onContextMenu, this);
40870         } else {
40871             this.el.on('mouseenter'  ,this.enter, this);
40872             this.el.on('mouseleave', this.leave, this);
40873             this.el.on('click', this.onClick, this);
40874         }
40875         
40876         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40877             this.parent().bricks.push(this);   
40878         }
40879         
40880     },
40881     
40882     onClick: function(e, el)
40883     {
40884         var time = this.endTimer - this.startTimer;
40885         // Roo.log(e.preventDefault());
40886         if(Roo.isTouch){
40887             if(time > 1000){
40888                 e.preventDefault();
40889                 return;
40890             }
40891         }
40892         
40893         if(!this.preventDefault){
40894             return;
40895         }
40896         
40897         e.preventDefault();
40898         
40899         if (this.activeClass != '') {
40900             this.selectBrick();
40901         }
40902         
40903         this.fireEvent('click', this, e);
40904     },
40905     
40906     enter: function(e, el)
40907     {
40908         e.preventDefault();
40909         
40910         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40911             return;
40912         }
40913         
40914         if(this.bgimage.length && this.html.length){
40915             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40916         }
40917     },
40918     
40919     leave: function(e, el)
40920     {
40921         e.preventDefault();
40922         
40923         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
40924             return;
40925         }
40926         
40927         if(this.bgimage.length && this.html.length){
40928             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40929         }
40930     },
40931     
40932     onTouchStart: function(e, el)
40933     {
40934 //        e.preventDefault();
40935         
40936         this.touchmoved = false;
40937         
40938         if(!this.isFitContainer){
40939             return;
40940         }
40941         
40942         if(!this.bgimage.length || !this.html.length){
40943             return;
40944         }
40945         
40946         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40947         
40948         this.timer = new Date().getTime();
40949         
40950     },
40951     
40952     onTouchMove: function(e, el)
40953     {
40954         this.touchmoved = true;
40955     },
40956     
40957     onContextMenu : function(e,el)
40958     {
40959         e.preventDefault();
40960         e.stopPropagation();
40961         return false;
40962     },
40963     
40964     onTouchEnd: function(e, el)
40965     {
40966 //        e.preventDefault();
40967         
40968         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40969         
40970             this.leave(e,el);
40971             
40972             return;
40973         }
40974         
40975         if(!this.bgimage.length || !this.html.length){
40976             
40977             if(this.href.length){
40978                 window.location.href = this.href;
40979             }
40980             
40981             return;
40982         }
40983         
40984         if(!this.isFitContainer){
40985             return;
40986         }
40987         
40988         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40989         
40990         window.location.href = this.href;
40991     },
40992     
40993     //selection on single brick only
40994     selectBrick : function() {
40995         
40996         if (!this.parentId) {
40997             return;
40998         }
40999         
41000         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41001         var index = m.selectedBrick.indexOf(this.id);
41002         
41003         if ( index > -1) {
41004             m.selectedBrick.splice(index,1);
41005             this.el.removeClass(this.activeClass);
41006             return;
41007         }
41008         
41009         for(var i = 0; i < m.selectedBrick.length; i++) {
41010             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41011             b.el.removeClass(b.activeClass);
41012         }
41013         
41014         m.selectedBrick = [];
41015         
41016         m.selectedBrick.push(this.id);
41017         this.el.addClass(this.activeClass);
41018         return;
41019     },
41020     
41021     isSelected : function(){
41022         return this.el.hasClass(this.activeClass);
41023         
41024     }
41025 });
41026
41027 Roo.apply(Roo.bootstrap.MasonryBrick, {
41028     
41029     //groups: {},
41030     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41031      /**
41032     * register a Masonry Brick
41033     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41034     */
41035     
41036     register : function(brick)
41037     {
41038         //this.groups[brick.id] = brick;
41039         this.groups.add(brick.id, brick);
41040     },
41041     /**
41042     * fetch a  masonry brick based on the masonry brick ID
41043     * @param {string} the masonry brick to add
41044     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41045     */
41046     
41047     get: function(brick_id) 
41048     {
41049         // if (typeof(this.groups[brick_id]) == 'undefined') {
41050         //     return false;
41051         // }
41052         // return this.groups[brick_id] ;
41053         
41054         if(this.groups.key(brick_id)) {
41055             return this.groups.key(brick_id);
41056         }
41057         
41058         return false;
41059     }
41060     
41061     
41062     
41063 });
41064
41065  /*
41066  * - LGPL
41067  *
41068  * element
41069  * 
41070  */
41071
41072 /**
41073  * @class Roo.bootstrap.Brick
41074  * @extends Roo.bootstrap.Component
41075  * Bootstrap Brick class
41076  * 
41077  * @constructor
41078  * Create a new Brick
41079  * @param {Object} config The config object
41080  */
41081
41082 Roo.bootstrap.Brick = function(config){
41083     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41084     
41085     this.addEvents({
41086         // raw events
41087         /**
41088          * @event click
41089          * When a Brick is click
41090          * @param {Roo.bootstrap.Brick} this
41091          * @param {Roo.EventObject} e
41092          */
41093         "click" : true
41094     });
41095 };
41096
41097 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
41098     
41099     /**
41100      * @cfg {String} title
41101      */   
41102     title : '',
41103     /**
41104      * @cfg {String} html
41105      */   
41106     html : '',
41107     /**
41108      * @cfg {String} bgimage
41109      */   
41110     bgimage : '',
41111     /**
41112      * @cfg {String} cls
41113      */   
41114     cls : '',
41115     /**
41116      * @cfg {String} href
41117      */   
41118     href : '',
41119     /**
41120      * @cfg {String} video
41121      */   
41122     video : '',
41123     /**
41124      * @cfg {Boolean} square
41125      */   
41126     square : true,
41127     
41128     getAutoCreate : function()
41129     {
41130         var cls = 'roo-brick';
41131         
41132         if(this.href.length){
41133             cls += ' roo-brick-link';
41134         }
41135         
41136         if(this.bgimage.length){
41137             cls += ' roo-brick-image';
41138         }
41139         
41140         if(!this.html.length && !this.bgimage.length){
41141             cls += ' roo-brick-center-title';
41142         }
41143         
41144         if(!this.html.length && this.bgimage.length){
41145             cls += ' roo-brick-bottom-title';
41146         }
41147         
41148         if(this.cls){
41149             cls += ' ' + this.cls;
41150         }
41151         
41152         var cfg = {
41153             tag: (this.href.length) ? 'a' : 'div',
41154             cls: cls,
41155             cn: [
41156                 {
41157                     tag: 'div',
41158                     cls: 'roo-brick-paragraph',
41159                     cn: []
41160                 }
41161             ]
41162         };
41163         
41164         if(this.href.length){
41165             cfg.href = this.href;
41166         }
41167         
41168         var cn = cfg.cn[0].cn;
41169         
41170         if(this.title.length){
41171             cn.push({
41172                 tag: 'h4',
41173                 cls: 'roo-brick-title',
41174                 html: this.title
41175             });
41176         }
41177         
41178         if(this.html.length){
41179             cn.push({
41180                 tag: 'p',
41181                 cls: 'roo-brick-text',
41182                 html: this.html
41183             });
41184         } else {
41185             cn.cls += ' hide';
41186         }
41187         
41188         if(this.bgimage.length){
41189             cfg.cn.push({
41190                 tag: 'img',
41191                 cls: 'roo-brick-image-view',
41192                 src: this.bgimage
41193             });
41194         }
41195         
41196         return cfg;
41197     },
41198     
41199     initEvents: function() 
41200     {
41201         if(this.title.length || this.html.length){
41202             this.el.on('mouseenter'  ,this.enter, this);
41203             this.el.on('mouseleave', this.leave, this);
41204         }
41205         
41206         Roo.EventManager.onWindowResize(this.resize, this); 
41207         
41208         if(this.bgimage.length){
41209             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41210             this.imageEl.on('load', this.onImageLoad, this);
41211             return;
41212         }
41213         
41214         this.resize();
41215     },
41216     
41217     onImageLoad : function()
41218     {
41219         this.resize();
41220     },
41221     
41222     resize : function()
41223     {
41224         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41225         
41226         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41227         
41228         if(this.bgimage.length){
41229             var image = this.el.select('.roo-brick-image-view', true).first();
41230             
41231             image.setWidth(paragraph.getWidth());
41232             
41233             if(this.square){
41234                 image.setHeight(paragraph.getWidth());
41235             }
41236             
41237             this.el.setHeight(image.getHeight());
41238             paragraph.setHeight(image.getHeight());
41239             
41240         }
41241         
41242     },
41243     
41244     enter: function(e, el)
41245     {
41246         e.preventDefault();
41247         
41248         if(this.bgimage.length){
41249             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41250             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41251         }
41252     },
41253     
41254     leave: function(e, el)
41255     {
41256         e.preventDefault();
41257         
41258         if(this.bgimage.length){
41259             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41260             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41261         }
41262     }
41263     
41264 });
41265
41266  
41267
41268  /*
41269  * - LGPL
41270  *
41271  * Number field 
41272  */
41273
41274 /**
41275  * @class Roo.bootstrap.form.NumberField
41276  * @extends Roo.bootstrap.form.Input
41277  * Bootstrap NumberField class
41278  * 
41279  * 
41280  * 
41281  * 
41282  * @constructor
41283  * Create a new NumberField
41284  * @param {Object} config The config object
41285  */
41286
41287 Roo.bootstrap.form.NumberField = function(config){
41288     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41289 };
41290
41291 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41292     
41293     /**
41294      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41295      */
41296     allowDecimals : true,
41297     /**
41298      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41299      */
41300     decimalSeparator : ".",
41301     /**
41302      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41303      */
41304     decimalPrecision : 2,
41305     /**
41306      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41307      */
41308     allowNegative : true,
41309     
41310     /**
41311      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41312      */
41313     allowZero: true,
41314     /**
41315      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41316      */
41317     minValue : Number.NEGATIVE_INFINITY,
41318     /**
41319      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41320      */
41321     maxValue : Number.MAX_VALUE,
41322     /**
41323      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41324      */
41325     minText : "The minimum value for this field is {0}",
41326     /**
41327      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41328      */
41329     maxText : "The maximum value for this field is {0}",
41330     /**
41331      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41332      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41333      */
41334     nanText : "{0} is not a valid number",
41335     /**
41336      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41337      */
41338     thousandsDelimiter : false,
41339     /**
41340      * @cfg {String} valueAlign alignment of value
41341      */
41342     valueAlign : "left",
41343
41344     getAutoCreate : function()
41345     {
41346         var hiddenInput = {
41347             tag: 'input',
41348             type: 'hidden',
41349             id: Roo.id(),
41350             cls: 'hidden-number-input'
41351         };
41352         
41353         if (this.name) {
41354             hiddenInput.name = this.name;
41355         }
41356         
41357         this.name = '';
41358         
41359         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41360         
41361         this.name = hiddenInput.name;
41362         
41363         if(cfg.cn.length > 0) {
41364             cfg.cn.push(hiddenInput);
41365         }
41366         
41367         return cfg;
41368     },
41369
41370     // private
41371     initEvents : function()
41372     {   
41373         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41374         
41375         var allowed = "0123456789";
41376         
41377         if(this.allowDecimals){
41378             allowed += this.decimalSeparator;
41379         }
41380         
41381         if(this.allowNegative){
41382             allowed += "-";
41383         }
41384         
41385         if(this.thousandsDelimiter) {
41386             allowed += ",";
41387         }
41388         
41389         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41390         
41391         var keyPress = function(e){
41392             
41393             var k = e.getKey();
41394             
41395             var c = e.getCharCode();
41396             
41397             if(
41398                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41399                     allowed.indexOf(String.fromCharCode(c)) === -1
41400             ){
41401                 e.stopEvent();
41402                 return;
41403             }
41404             
41405             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41406                 return;
41407             }
41408             
41409             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41410                 e.stopEvent();
41411             }
41412         };
41413         
41414         this.el.on("keypress", keyPress, this);
41415     },
41416     
41417     validateValue : function(value)
41418     {
41419         
41420         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41421             return false;
41422         }
41423         
41424         var num = this.parseValue(value);
41425         
41426         if(isNaN(num)){
41427             this.markInvalid(String.format(this.nanText, value));
41428             return false;
41429         }
41430         
41431         if(num < this.minValue){
41432             this.markInvalid(String.format(this.minText, this.minValue));
41433             return false;
41434         }
41435         
41436         if(num > this.maxValue){
41437             this.markInvalid(String.format(this.maxText, this.maxValue));
41438             return false;
41439         }
41440         
41441         return true;
41442     },
41443
41444     getValue : function()
41445     {
41446         var v = this.hiddenEl().getValue();
41447         
41448         return this.fixPrecision(this.parseValue(v));
41449     },
41450
41451     parseValue : function(value)
41452     {
41453         if(this.thousandsDelimiter) {
41454             value += "";
41455             r = new RegExp(",", "g");
41456             value = value.replace(r, "");
41457         }
41458         
41459         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41460         return isNaN(value) ? '' : value;
41461     },
41462
41463     fixPrecision : function(value)
41464     {
41465         if(this.thousandsDelimiter) {
41466             value += "";
41467             r = new RegExp(",", "g");
41468             value = value.replace(r, "");
41469         }
41470         
41471         var nan = isNaN(value);
41472         
41473         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41474             return nan ? '' : value;
41475         }
41476         return parseFloat(value).toFixed(this.decimalPrecision);
41477     },
41478
41479     setValue : function(v)
41480     {
41481         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41482         
41483         this.value = v;
41484         
41485         if(this.rendered){
41486             
41487             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41488             
41489             this.inputEl().dom.value = (v == '') ? '' :
41490                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41491             
41492             if(!this.allowZero && v === '0') {
41493                 this.hiddenEl().dom.value = '';
41494                 this.inputEl().dom.value = '';
41495             }
41496             
41497             this.validate();
41498         }
41499     },
41500
41501     decimalPrecisionFcn : function(v)
41502     {
41503         return Math.floor(v);
41504     },
41505
41506     beforeBlur : function()
41507     {
41508         var v = this.parseValue(this.getRawValue());
41509         
41510         if(v || v === 0 || v === ''){
41511             this.setValue(v);
41512         }
41513     },
41514     
41515     hiddenEl : function()
41516     {
41517         return this.el.select('input.hidden-number-input',true).first();
41518     }
41519     
41520 });
41521
41522  
41523
41524 /*
41525 * Licence: LGPL
41526 */
41527
41528 /**
41529  * @class Roo.bootstrap.DocumentSlider
41530  * @extends Roo.bootstrap.Component
41531  * Bootstrap DocumentSlider class
41532  * 
41533  * @constructor
41534  * Create a new DocumentViewer
41535  * @param {Object} config The config object
41536  */
41537
41538 Roo.bootstrap.DocumentSlider = function(config){
41539     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41540     
41541     this.files = [];
41542     
41543     this.addEvents({
41544         /**
41545          * @event initial
41546          * Fire after initEvent
41547          * @param {Roo.bootstrap.DocumentSlider} this
41548          */
41549         "initial" : true,
41550         /**
41551          * @event update
41552          * Fire after update
41553          * @param {Roo.bootstrap.DocumentSlider} this
41554          */
41555         "update" : true,
41556         /**
41557          * @event click
41558          * Fire after click
41559          * @param {Roo.bootstrap.DocumentSlider} this
41560          */
41561         "click" : true
41562     });
41563 };
41564
41565 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41566     
41567     files : false,
41568     
41569     indicator : 0,
41570     
41571     getAutoCreate : function()
41572     {
41573         var cfg = {
41574             tag : 'div',
41575             cls : 'roo-document-slider',
41576             cn : [
41577                 {
41578                     tag : 'div',
41579                     cls : 'roo-document-slider-header',
41580                     cn : [
41581                         {
41582                             tag : 'div',
41583                             cls : 'roo-document-slider-header-title'
41584                         }
41585                     ]
41586                 },
41587                 {
41588                     tag : 'div',
41589                     cls : 'roo-document-slider-body',
41590                     cn : [
41591                         {
41592                             tag : 'div',
41593                             cls : 'roo-document-slider-prev',
41594                             cn : [
41595                                 {
41596                                     tag : 'i',
41597                                     cls : 'fa fa-chevron-left'
41598                                 }
41599                             ]
41600                         },
41601                         {
41602                             tag : 'div',
41603                             cls : 'roo-document-slider-thumb',
41604                             cn : [
41605                                 {
41606                                     tag : 'img',
41607                                     cls : 'roo-document-slider-image'
41608                                 }
41609                             ]
41610                         },
41611                         {
41612                             tag : 'div',
41613                             cls : 'roo-document-slider-next',
41614                             cn : [
41615                                 {
41616                                     tag : 'i',
41617                                     cls : 'fa fa-chevron-right'
41618                                 }
41619                             ]
41620                         }
41621                     ]
41622                 }
41623             ]
41624         };
41625         
41626         return cfg;
41627     },
41628     
41629     initEvents : function()
41630     {
41631         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41632         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41633         
41634         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41635         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41636         
41637         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41638         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41639         
41640         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41641         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41642         
41643         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41644         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41645         
41646         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41647         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41648         
41649         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41650         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41651         
41652         this.thumbEl.on('click', this.onClick, this);
41653         
41654         this.prevIndicator.on('click', this.prev, this);
41655         
41656         this.nextIndicator.on('click', this.next, this);
41657         
41658     },
41659     
41660     initial : function()
41661     {
41662         if(this.files.length){
41663             this.indicator = 1;
41664             this.update()
41665         }
41666         
41667         this.fireEvent('initial', this);
41668     },
41669     
41670     update : function()
41671     {
41672         this.imageEl.attr('src', this.files[this.indicator - 1]);
41673         
41674         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41675         
41676         this.prevIndicator.show();
41677         
41678         if(this.indicator == 1){
41679             this.prevIndicator.hide();
41680         }
41681         
41682         this.nextIndicator.show();
41683         
41684         if(this.indicator == this.files.length){
41685             this.nextIndicator.hide();
41686         }
41687         
41688         this.thumbEl.scrollTo('top');
41689         
41690         this.fireEvent('update', this);
41691     },
41692     
41693     onClick : function(e)
41694     {
41695         e.preventDefault();
41696         
41697         this.fireEvent('click', this);
41698     },
41699     
41700     prev : function(e)
41701     {
41702         e.preventDefault();
41703         
41704         this.indicator = Math.max(1, this.indicator - 1);
41705         
41706         this.update();
41707     },
41708     
41709     next : function(e)
41710     {
41711         e.preventDefault();
41712         
41713         this.indicator = Math.min(this.files.length, this.indicator + 1);
41714         
41715         this.update();
41716     }
41717 });
41718 /*
41719  * - LGPL
41720  *
41721  * RadioSet
41722  *
41723  *
41724  */
41725
41726 /**
41727  * @class Roo.bootstrap.form.RadioSet
41728  * @extends Roo.bootstrap.form.Input
41729  * @children Roo.bootstrap.form.Radio
41730  * Bootstrap RadioSet class
41731  * @cfg {String} indicatorpos (left|right) default left
41732  * @cfg {Boolean} inline (true|false) inline the element (default true)
41733  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41734  * @constructor
41735  * Create a new RadioSet
41736  * @param {Object} config The config object
41737  */
41738
41739 Roo.bootstrap.form.RadioSet = function(config){
41740     
41741     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41742     
41743     this.radioes = [];
41744     
41745     Roo.bootstrap.form.RadioSet.register(this);
41746     
41747     this.addEvents({
41748         /**
41749         * @event check
41750         * Fires when the element is checked or unchecked.
41751         * @param {Roo.bootstrap.form.RadioSet} this This radio
41752         * @param {Roo.bootstrap.form.Radio} item The checked item
41753         */
41754        check : true,
41755        /**
41756         * @event click
41757         * Fires when the element is click.
41758         * @param {Roo.bootstrap.form.RadioSet} this This radio set
41759         * @param {Roo.bootstrap.form.Radio} item The checked item
41760         * @param {Roo.EventObject} e The event object
41761         */
41762        click : true
41763     });
41764     
41765 };
41766
41767 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
41768
41769     radioes : false,
41770     
41771     inline : true,
41772     
41773     weight : '',
41774     
41775     indicatorpos : 'left',
41776     
41777     getAutoCreate : function()
41778     {
41779         var label = {
41780             tag : 'label',
41781             cls : 'roo-radio-set-label',
41782             cn : [
41783                 {
41784                     tag : 'span',
41785                     html : this.fieldLabel
41786                 }
41787             ]
41788         };
41789         if (Roo.bootstrap.version == 3) {
41790             
41791             
41792             if(this.indicatorpos == 'left'){
41793                 label.cn.unshift({
41794                     tag : 'i',
41795                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41796                     tooltip : 'This field is required'
41797                 });
41798             } else {
41799                 label.cn.push({
41800                     tag : 'i',
41801                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41802                     tooltip : 'This field is required'
41803                 });
41804             }
41805         }
41806         var items = {
41807             tag : 'div',
41808             cls : 'roo-radio-set-items'
41809         };
41810         
41811         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41812         
41813         if (align === 'left' && this.fieldLabel.length) {
41814             
41815             items = {
41816                 cls : "roo-radio-set-right", 
41817                 cn: [
41818                     items
41819                 ]
41820             };
41821             
41822             if(this.labelWidth > 12){
41823                 label.style = "width: " + this.labelWidth + 'px';
41824             }
41825             
41826             if(this.labelWidth < 13 && this.labelmd == 0){
41827                 this.labelmd = this.labelWidth;
41828             }
41829             
41830             if(this.labellg > 0){
41831                 label.cls += ' col-lg-' + this.labellg;
41832                 items.cls += ' col-lg-' + (12 - this.labellg);
41833             }
41834             
41835             if(this.labelmd > 0){
41836                 label.cls += ' col-md-' + this.labelmd;
41837                 items.cls += ' col-md-' + (12 - this.labelmd);
41838             }
41839             
41840             if(this.labelsm > 0){
41841                 label.cls += ' col-sm-' + this.labelsm;
41842                 items.cls += ' col-sm-' + (12 - this.labelsm);
41843             }
41844             
41845             if(this.labelxs > 0){
41846                 label.cls += ' col-xs-' + this.labelxs;
41847                 items.cls += ' col-xs-' + (12 - this.labelxs);
41848             }
41849         }
41850         
41851         var cfg = {
41852             tag : 'div',
41853             cls : 'roo-radio-set',
41854             cn : [
41855                 {
41856                     tag : 'input',
41857                     cls : 'roo-radio-set-input',
41858                     type : 'hidden',
41859                     name : this.name,
41860                     value : this.value ? this.value :  ''
41861                 },
41862                 label,
41863                 items
41864             ]
41865         };
41866         
41867         if(this.weight.length){
41868             cfg.cls += ' roo-radio-' + this.weight;
41869         }
41870         
41871         if(this.inline) {
41872             cfg.cls += ' roo-radio-set-inline';
41873         }
41874         
41875         var settings=this;
41876         ['xs','sm','md','lg'].map(function(size){
41877             if (settings[size]) {
41878                 cfg.cls += ' col-' + size + '-' + settings[size];
41879             }
41880         });
41881         
41882         return cfg;
41883         
41884     },
41885
41886     initEvents : function()
41887     {
41888         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41889         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41890         
41891         if(!this.fieldLabel.length){
41892             this.labelEl.hide();
41893         }
41894         
41895         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41896         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41897         
41898         this.indicator = this.indicatorEl();
41899         
41900         if(this.indicator){
41901             this.indicator.addClass('invisible');
41902         }
41903         
41904         this.originalValue = this.getValue();
41905         
41906     },
41907     
41908     inputEl: function ()
41909     {
41910         return this.el.select('.roo-radio-set-input', true).first();
41911     },
41912     
41913     getChildContainer : function()
41914     {
41915         return this.itemsEl;
41916     },
41917     
41918     register : function(item)
41919     {
41920         this.radioes.push(item);
41921         
41922     },
41923     
41924     validate : function()
41925     {   
41926         if(this.getVisibilityEl().hasClass('hidden')){
41927             return true;
41928         }
41929         
41930         var valid = false;
41931         
41932         Roo.each(this.radioes, function(i){
41933             if(!i.checked){
41934                 return;
41935             }
41936             
41937             valid = true;
41938             return false;
41939         });
41940         
41941         if(this.allowBlank) {
41942             return true;
41943         }
41944         
41945         if(this.disabled || valid){
41946             this.markValid();
41947             return true;
41948         }
41949         
41950         this.markInvalid();
41951         return false;
41952         
41953     },
41954     
41955     markValid : function()
41956     {
41957         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41958             this.indicatorEl().removeClass('visible');
41959             this.indicatorEl().addClass('invisible');
41960         }
41961         
41962         
41963         if (Roo.bootstrap.version == 3) {
41964             this.el.removeClass([this.invalidClass, this.validClass]);
41965             this.el.addClass(this.validClass);
41966         } else {
41967             this.el.removeClass(['is-invalid','is-valid']);
41968             this.el.addClass(['is-valid']);
41969         }
41970         this.fireEvent('valid', this);
41971     },
41972     
41973     markInvalid : function(msg)
41974     {
41975         if(this.allowBlank || this.disabled){
41976             return;
41977         }
41978         
41979         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41980             this.indicatorEl().removeClass('invisible');
41981             this.indicatorEl().addClass('visible');
41982         }
41983         if (Roo.bootstrap.version == 3) {
41984             this.el.removeClass([this.invalidClass, this.validClass]);
41985             this.el.addClass(this.invalidClass);
41986         } else {
41987             this.el.removeClass(['is-invalid','is-valid']);
41988             this.el.addClass(['is-invalid']);
41989         }
41990         
41991         this.fireEvent('invalid', this, msg);
41992         
41993     },
41994     
41995     setValue : function(v, suppressEvent)
41996     {   
41997         if(this.value === v){
41998             return;
41999         }
42000         
42001         this.value = v;
42002         
42003         if(this.rendered){
42004             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42005         }
42006         
42007         Roo.each(this.radioes, function(i){
42008             i.checked = false;
42009             i.el.removeClass('checked');
42010         });
42011         
42012         Roo.each(this.radioes, function(i){
42013             
42014             if(i.value === v || i.value.toString() === v.toString()){
42015                 i.checked = true;
42016                 i.el.addClass('checked');
42017                 
42018                 if(suppressEvent !== true){
42019                     this.fireEvent('check', this, i);
42020                 }
42021                 
42022                 return false;
42023             }
42024             
42025         }, this);
42026         
42027         this.validate();
42028     },
42029     
42030     clearInvalid : function(){
42031         
42032         if(!this.el || this.preventMark){
42033             return;
42034         }
42035         
42036         this.el.removeClass([this.invalidClass]);
42037         
42038         this.fireEvent('valid', this);
42039     }
42040     
42041 });
42042
42043 Roo.apply(Roo.bootstrap.form.RadioSet, {
42044     
42045     groups: {},
42046     
42047     register : function(set)
42048     {
42049         this.groups[set.name] = set;
42050     },
42051     
42052     get: function(name) 
42053     {
42054         if (typeof(this.groups[name]) == 'undefined') {
42055             return false;
42056         }
42057         
42058         return this.groups[name] ;
42059     }
42060     
42061 });
42062 /*
42063  * Based on:
42064  * Ext JS Library 1.1.1
42065  * Copyright(c) 2006-2007, Ext JS, LLC.
42066  *
42067  * Originally Released Under LGPL - original licence link has changed is not relivant.
42068  *
42069  * Fork - LGPL
42070  * <script type="text/javascript">
42071  */
42072
42073
42074 /**
42075  * @class Roo.bootstrap.SplitBar
42076  * @extends Roo.util.Observable
42077  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42078  * <br><br>
42079  * Usage:
42080  * <pre><code>
42081 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42082                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42083 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42084 split.minSize = 100;
42085 split.maxSize = 600;
42086 split.animate = true;
42087 split.on('moved', splitterMoved);
42088 </code></pre>
42089  * @constructor
42090  * Create a new SplitBar
42091  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
42092  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
42093  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42094  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
42095                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42096                         position of the SplitBar).
42097  */
42098 Roo.bootstrap.SplitBar = function(cfg){
42099     
42100     /** @private */
42101     
42102     //{
42103     //  dragElement : elm
42104     //  resizingElement: el,
42105         // optional..
42106     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42107     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
42108         // existingProxy ???
42109     //}
42110     
42111     this.el = Roo.get(cfg.dragElement, true);
42112     this.el.dom.unselectable = "on";
42113     /** @private */
42114     this.resizingEl = Roo.get(cfg.resizingElement, true);
42115
42116     /**
42117      * @private
42118      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42119      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42120      * @type Number
42121      */
42122     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42123     
42124     /**
42125      * The minimum size of the resizing element. (Defaults to 0)
42126      * @type Number
42127      */
42128     this.minSize = 0;
42129     
42130     /**
42131      * The maximum size of the resizing element. (Defaults to 2000)
42132      * @type Number
42133      */
42134     this.maxSize = 2000;
42135     
42136     /**
42137      * Whether to animate the transition to the new size
42138      * @type Boolean
42139      */
42140     this.animate = false;
42141     
42142     /**
42143      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42144      * @type Boolean
42145      */
42146     this.useShim = false;
42147     
42148     /** @private */
42149     this.shim = null;
42150     
42151     if(!cfg.existingProxy){
42152         /** @private */
42153         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42154     }else{
42155         this.proxy = Roo.get(cfg.existingProxy).dom;
42156     }
42157     /** @private */
42158     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42159     
42160     /** @private */
42161     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42162     
42163     /** @private */
42164     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42165     
42166     /** @private */
42167     this.dragSpecs = {};
42168     
42169     /**
42170      * @private The adapter to use to positon and resize elements
42171      */
42172     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42173     this.adapter.init(this);
42174     
42175     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42176         /** @private */
42177         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42178         this.el.addClass("roo-splitbar-h");
42179     }else{
42180         /** @private */
42181         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42182         this.el.addClass("roo-splitbar-v");
42183     }
42184     
42185     this.addEvents({
42186         /**
42187          * @event resize
42188          * Fires when the splitter is moved (alias for {@link #event-moved})
42189          * @param {Roo.bootstrap.SplitBar} this
42190          * @param {Number} newSize the new width or height
42191          */
42192         "resize" : true,
42193         /**
42194          * @event moved
42195          * Fires when the splitter is moved
42196          * @param {Roo.bootstrap.SplitBar} this
42197          * @param {Number} newSize the new width or height
42198          */
42199         "moved" : true,
42200         /**
42201          * @event beforeresize
42202          * Fires before the splitter is dragged
42203          * @param {Roo.bootstrap.SplitBar} this
42204          */
42205         "beforeresize" : true,
42206
42207         "beforeapply" : true
42208     });
42209
42210     Roo.util.Observable.call(this);
42211 };
42212
42213 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42214     onStartProxyDrag : function(x, y){
42215         this.fireEvent("beforeresize", this);
42216         if(!this.overlay){
42217             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
42218             o.unselectable();
42219             o.enableDisplayMode("block");
42220             // all splitbars share the same overlay
42221             Roo.bootstrap.SplitBar.prototype.overlay = o;
42222         }
42223         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42224         this.overlay.show();
42225         Roo.get(this.proxy).setDisplayed("block");
42226         var size = this.adapter.getElementSize(this);
42227         this.activeMinSize = this.getMinimumSize();;
42228         this.activeMaxSize = this.getMaximumSize();;
42229         var c1 = size - this.activeMinSize;
42230         var c2 = Math.max(this.activeMaxSize - size, 0);
42231         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42232             this.dd.resetConstraints();
42233             this.dd.setXConstraint(
42234                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
42235                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42236             );
42237             this.dd.setYConstraint(0, 0);
42238         }else{
42239             this.dd.resetConstraints();
42240             this.dd.setXConstraint(0, 0);
42241             this.dd.setYConstraint(
42242                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
42243                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42244             );
42245          }
42246         this.dragSpecs.startSize = size;
42247         this.dragSpecs.startPoint = [x, y];
42248         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42249     },
42250     
42251     /** 
42252      * @private Called after the drag operation by the DDProxy
42253      */
42254     onEndProxyDrag : function(e){
42255         Roo.get(this.proxy).setDisplayed(false);
42256         var endPoint = Roo.lib.Event.getXY(e);
42257         if(this.overlay){
42258             this.overlay.hide();
42259         }
42260         var newSize;
42261         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42262             newSize = this.dragSpecs.startSize + 
42263                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42264                     endPoint[0] - this.dragSpecs.startPoint[0] :
42265                     this.dragSpecs.startPoint[0] - endPoint[0]
42266                 );
42267         }else{
42268             newSize = this.dragSpecs.startSize + 
42269                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42270                     endPoint[1] - this.dragSpecs.startPoint[1] :
42271                     this.dragSpecs.startPoint[1] - endPoint[1]
42272                 );
42273         }
42274         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42275         if(newSize != this.dragSpecs.startSize){
42276             if(this.fireEvent('beforeapply', this, newSize) !== false){
42277                 this.adapter.setElementSize(this, newSize);
42278                 this.fireEvent("moved", this, newSize);
42279                 this.fireEvent("resize", this, newSize);
42280             }
42281         }
42282     },
42283     
42284     /**
42285      * Get the adapter this SplitBar uses
42286      * @return The adapter object
42287      */
42288     getAdapter : function(){
42289         return this.adapter;
42290     },
42291     
42292     /**
42293      * Set the adapter this SplitBar uses
42294      * @param {Object} adapter A SplitBar adapter object
42295      */
42296     setAdapter : function(adapter){
42297         this.adapter = adapter;
42298         this.adapter.init(this);
42299     },
42300     
42301     /**
42302      * Gets the minimum size for the resizing element
42303      * @return {Number} The minimum size
42304      */
42305     getMinimumSize : function(){
42306         return this.minSize;
42307     },
42308     
42309     /**
42310      * Sets the minimum size for the resizing element
42311      * @param {Number} minSize The minimum size
42312      */
42313     setMinimumSize : function(minSize){
42314         this.minSize = minSize;
42315     },
42316     
42317     /**
42318      * Gets the maximum size for the resizing element
42319      * @return {Number} The maximum size
42320      */
42321     getMaximumSize : function(){
42322         return this.maxSize;
42323     },
42324     
42325     /**
42326      * Sets the maximum size for the resizing element
42327      * @param {Number} maxSize The maximum size
42328      */
42329     setMaximumSize : function(maxSize){
42330         this.maxSize = maxSize;
42331     },
42332     
42333     /**
42334      * Sets the initialize size for the resizing element
42335      * @param {Number} size The initial size
42336      */
42337     setCurrentSize : function(size){
42338         var oldAnimate = this.animate;
42339         this.animate = false;
42340         this.adapter.setElementSize(this, size);
42341         this.animate = oldAnimate;
42342     },
42343     
42344     /**
42345      * Destroy this splitbar. 
42346      * @param {Boolean} removeEl True to remove the element
42347      */
42348     destroy : function(removeEl){
42349         if(this.shim){
42350             this.shim.remove();
42351         }
42352         this.dd.unreg();
42353         this.proxy.parentNode.removeChild(this.proxy);
42354         if(removeEl){
42355             this.el.remove();
42356         }
42357     }
42358 });
42359
42360 /**
42361  * @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.
42362  */
42363 Roo.bootstrap.SplitBar.createProxy = function(dir){
42364     var proxy = new Roo.Element(document.createElement("div"));
42365     proxy.unselectable();
42366     var cls = 'roo-splitbar-proxy';
42367     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42368     document.body.appendChild(proxy.dom);
42369     return proxy.dom;
42370 };
42371
42372 /** 
42373  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42374  * Default Adapter. It assumes the splitter and resizing element are not positioned
42375  * elements and only gets/sets the width of the element. Generally used for table based layouts.
42376  */
42377 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42378 };
42379
42380 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42381     // do nothing for now
42382     init : function(s){
42383     
42384     },
42385     /**
42386      * Called before drag operations to get the current size of the resizing element. 
42387      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42388      */
42389      getElementSize : function(s){
42390         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42391             return s.resizingEl.getWidth();
42392         }else{
42393             return s.resizingEl.getHeight();
42394         }
42395     },
42396     
42397     /**
42398      * Called after drag operations to set the size of the resizing element.
42399      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42400      * @param {Number} newSize The new size to set
42401      * @param {Function} onComplete A function to be invoked when resizing is complete
42402      */
42403     setElementSize : function(s, newSize, onComplete){
42404         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42405             if(!s.animate){
42406                 s.resizingEl.setWidth(newSize);
42407                 if(onComplete){
42408                     onComplete(s, newSize);
42409                 }
42410             }else{
42411                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42412             }
42413         }else{
42414             
42415             if(!s.animate){
42416                 s.resizingEl.setHeight(newSize);
42417                 if(onComplete){
42418                     onComplete(s, newSize);
42419                 }
42420             }else{
42421                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42422             }
42423         }
42424     }
42425 };
42426
42427 /** 
42428  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42429  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42430  * Adapter that  moves the splitter element to align with the resized sizing element. 
42431  * Used with an absolute positioned SplitBar.
42432  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42433  * document.body, make sure you assign an id to the body element.
42434  */
42435 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42436     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42437     this.container = Roo.get(container);
42438 };
42439
42440 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42441     init : function(s){
42442         this.basic.init(s);
42443     },
42444     
42445     getElementSize : function(s){
42446         return this.basic.getElementSize(s);
42447     },
42448     
42449     setElementSize : function(s, newSize, onComplete){
42450         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42451     },
42452     
42453     moveSplitter : function(s){
42454         var yes = Roo.bootstrap.SplitBar;
42455         switch(s.placement){
42456             case yes.LEFT:
42457                 s.el.setX(s.resizingEl.getRight());
42458                 break;
42459             case yes.RIGHT:
42460                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42461                 break;
42462             case yes.TOP:
42463                 s.el.setY(s.resizingEl.getBottom());
42464                 break;
42465             case yes.BOTTOM:
42466                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42467                 break;
42468         }
42469     }
42470 };
42471
42472 /**
42473  * Orientation constant - Create a vertical SplitBar
42474  * @static
42475  * @type Number
42476  */
42477 Roo.bootstrap.SplitBar.VERTICAL = 1;
42478
42479 /**
42480  * Orientation constant - Create a horizontal SplitBar
42481  * @static
42482  * @type Number
42483  */
42484 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42485
42486 /**
42487  * Placement constant - The resizing element is to the left of the splitter element
42488  * @static
42489  * @type Number
42490  */
42491 Roo.bootstrap.SplitBar.LEFT = 1;
42492
42493 /**
42494  * Placement constant - The resizing element is to the right of the splitter element
42495  * @static
42496  * @type Number
42497  */
42498 Roo.bootstrap.SplitBar.RIGHT = 2;
42499
42500 /**
42501  * Placement constant - The resizing element is positioned above the splitter element
42502  * @static
42503  * @type Number
42504  */
42505 Roo.bootstrap.SplitBar.TOP = 3;
42506
42507 /**
42508  * Placement constant - The resizing element is positioned under splitter element
42509  * @static
42510  * @type Number
42511  */
42512 Roo.bootstrap.SplitBar.BOTTOM = 4;
42513 /*
42514  * Based on:
42515  * Ext JS Library 1.1.1
42516  * Copyright(c) 2006-2007, Ext JS, LLC.
42517  *
42518  * Originally Released Under LGPL - original licence link has changed is not relivant.
42519  *
42520  * Fork - LGPL
42521  * <script type="text/javascript">
42522  */
42523
42524 /**
42525  * @class Roo.bootstrap.layout.Manager
42526  * @extends Roo.bootstrap.Component
42527  * @abstract
42528  * Base class for layout managers.
42529  */
42530 Roo.bootstrap.layout.Manager = function(config)
42531 {
42532     this.monitorWindowResize = true; // do this before we apply configuration.
42533     
42534     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42535
42536
42537
42538
42539
42540     /** false to disable window resize monitoring @type Boolean */
42541     
42542     this.regions = {};
42543     this.addEvents({
42544         /**
42545          * @event layout
42546          * Fires when a layout is performed.
42547          * @param {Roo.LayoutManager} this
42548          */
42549         "layout" : true,
42550         /**
42551          * @event regionresized
42552          * Fires when the user resizes a region.
42553          * @param {Roo.LayoutRegion} region The resized region
42554          * @param {Number} newSize The new size (width for east/west, height for north/south)
42555          */
42556         "regionresized" : true,
42557         /**
42558          * @event regioncollapsed
42559          * Fires when a region is collapsed.
42560          * @param {Roo.LayoutRegion} region The collapsed region
42561          */
42562         "regioncollapsed" : true,
42563         /**
42564          * @event regionexpanded
42565          * Fires when a region is expanded.
42566          * @param {Roo.LayoutRegion} region The expanded region
42567          */
42568         "regionexpanded" : true
42569     });
42570     this.updating = false;
42571
42572     if (config.el) {
42573         this.el = Roo.get(config.el);
42574         this.initEvents();
42575     }
42576
42577 };
42578
42579 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42580
42581
42582     regions : null,
42583
42584     monitorWindowResize : true,
42585
42586
42587     updating : false,
42588
42589
42590     onRender : function(ct, position)
42591     {
42592         if(!this.el){
42593             this.el = Roo.get(ct);
42594             this.initEvents();
42595         }
42596         //this.fireEvent('render',this);
42597     },
42598
42599
42600     initEvents: function()
42601     {
42602
42603
42604         // ie scrollbar fix
42605         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42606             document.body.scroll = "no";
42607         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42608             this.el.position('relative');
42609         }
42610         this.id = this.el.id;
42611         this.el.addClass("roo-layout-container");
42612         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42613         if(this.el.dom != document.body ) {
42614             this.el.on('resize', this.layout,this);
42615             this.el.on('show', this.layout,this);
42616         }
42617
42618     },
42619
42620     /**
42621      * Returns true if this layout is currently being updated
42622      * @return {Boolean}
42623      */
42624     isUpdating : function(){
42625         return this.updating;
42626     },
42627
42628     /**
42629      * Suspend the LayoutManager from doing auto-layouts while
42630      * making multiple add or remove calls
42631      */
42632     beginUpdate : function(){
42633         this.updating = true;
42634     },
42635
42636     /**
42637      * Restore auto-layouts and optionally disable the manager from performing a layout
42638      * @param {Boolean} noLayout true to disable a layout update
42639      */
42640     endUpdate : function(noLayout){
42641         this.updating = false;
42642         if(!noLayout){
42643             this.layout();
42644         }
42645     },
42646
42647     layout: function(){
42648         // abstract...
42649     },
42650
42651     onRegionResized : function(region, newSize){
42652         this.fireEvent("regionresized", region, newSize);
42653         this.layout();
42654     },
42655
42656     onRegionCollapsed : function(region){
42657         this.fireEvent("regioncollapsed", region);
42658     },
42659
42660     onRegionExpanded : function(region){
42661         this.fireEvent("regionexpanded", region);
42662     },
42663
42664     /**
42665      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42666      * performs box-model adjustments.
42667      * @return {Object} The size as an object {width: (the width), height: (the height)}
42668      */
42669     getViewSize : function()
42670     {
42671         var size;
42672         if(this.el.dom != document.body){
42673             size = this.el.getSize();
42674         }else{
42675             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42676         }
42677         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42678         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42679         return size;
42680     },
42681
42682     /**
42683      * Returns the Element this layout is bound to.
42684      * @return {Roo.Element}
42685      */
42686     getEl : function(){
42687         return this.el;
42688     },
42689
42690     /**
42691      * Returns the specified region.
42692      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42693      * @return {Roo.LayoutRegion}
42694      */
42695     getRegion : function(target){
42696         return this.regions[target.toLowerCase()];
42697     },
42698
42699     onWindowResize : function(){
42700         if(this.monitorWindowResize){
42701             this.layout();
42702         }
42703     }
42704 });
42705 /*
42706  * Based on:
42707  * Ext JS Library 1.1.1
42708  * Copyright(c) 2006-2007, Ext JS, LLC.
42709  *
42710  * Originally Released Under LGPL - original licence link has changed is not relivant.
42711  *
42712  * Fork - LGPL
42713  * <script type="text/javascript">
42714  */
42715 /**
42716  * @class Roo.bootstrap.layout.Border
42717  * @extends Roo.bootstrap.layout.Manager
42718  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42719  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42720  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42721  * please see: examples/bootstrap/nested.html<br><br>
42722  
42723 <b>The container the layout is rendered into can be either the body element or any other element.
42724 If it is not the body element, the container needs to either be an absolute positioned element,
42725 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42726 the container size if it is not the body element.</b>
42727
42728 * @constructor
42729 * Create a new Border
42730 * @param {Object} config Configuration options
42731  */
42732 Roo.bootstrap.layout.Border = function(config){
42733     config = config || {};
42734     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42735     
42736     
42737     
42738     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42739         if(config[region]){
42740             config[region].region = region;
42741             this.addRegion(config[region]);
42742         }
42743     },this);
42744     
42745 };
42746
42747 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
42748
42749 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42750     
42751         /**
42752          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42753          */
42754         /**
42755          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42756          */
42757         /**
42758          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42759          */
42760         /**
42761          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42762          */
42763         /**
42764          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42765          */
42766         
42767         
42768         
42769         
42770     parent : false, // this might point to a 'nest' or a ???
42771     
42772     /**
42773      * Creates and adds a new region if it doesn't already exist.
42774      * @param {String} target The target region key (north, south, east, west or center).
42775      * @param {Object} config The regions config object
42776      * @return {BorderLayoutRegion} The new region
42777      */
42778     addRegion : function(config)
42779     {
42780         if(!this.regions[config.region]){
42781             var r = this.factory(config);
42782             this.bindRegion(r);
42783         }
42784         return this.regions[config.region];
42785     },
42786
42787     // private (kinda)
42788     bindRegion : function(r){
42789         this.regions[r.config.region] = r;
42790         
42791         r.on("visibilitychange",    this.layout, this);
42792         r.on("paneladded",          this.layout, this);
42793         r.on("panelremoved",        this.layout, this);
42794         r.on("invalidated",         this.layout, this);
42795         r.on("resized",             this.onRegionResized, this);
42796         r.on("collapsed",           this.onRegionCollapsed, this);
42797         r.on("expanded",            this.onRegionExpanded, this);
42798     },
42799
42800     /**
42801      * Performs a layout update.
42802      */
42803     layout : function()
42804     {
42805         if(this.updating) {
42806             return;
42807         }
42808         
42809         // render all the rebions if they have not been done alreayd?
42810         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42811             if(this.regions[region] && !this.regions[region].bodyEl){
42812                 this.regions[region].onRender(this.el)
42813             }
42814         },this);
42815         
42816         var size = this.getViewSize();
42817         var w = size.width;
42818         var h = size.height;
42819         var centerW = w;
42820         var centerH = h;
42821         var centerY = 0;
42822         var centerX = 0;
42823         //var x = 0, y = 0;
42824
42825         var rs = this.regions;
42826         var north = rs["north"];
42827         var south = rs["south"]; 
42828         var west = rs["west"];
42829         var east = rs["east"];
42830         var center = rs["center"];
42831         //if(this.hideOnLayout){ // not supported anymore
42832             //c.el.setStyle("display", "none");
42833         //}
42834         if(north && north.isVisible()){
42835             var b = north.getBox();
42836             var m = north.getMargins();
42837             b.width = w - (m.left+m.right);
42838             b.x = m.left;
42839             b.y = m.top;
42840             centerY = b.height + b.y + m.bottom;
42841             centerH -= centerY;
42842             north.updateBox(this.safeBox(b));
42843         }
42844         if(south && south.isVisible()){
42845             var b = south.getBox();
42846             var m = south.getMargins();
42847             b.width = w - (m.left+m.right);
42848             b.x = m.left;
42849             var totalHeight = (b.height + m.top + m.bottom);
42850             b.y = h - totalHeight + m.top;
42851             centerH -= totalHeight;
42852             south.updateBox(this.safeBox(b));
42853         }
42854         if(west && west.isVisible()){
42855             var b = west.getBox();
42856             var m = west.getMargins();
42857             b.height = centerH - (m.top+m.bottom);
42858             b.x = m.left;
42859             b.y = centerY + m.top;
42860             var totalWidth = (b.width + m.left + m.right);
42861             centerX += totalWidth;
42862             centerW -= totalWidth;
42863             west.updateBox(this.safeBox(b));
42864         }
42865         if(east && east.isVisible()){
42866             var b = east.getBox();
42867             var m = east.getMargins();
42868             b.height = centerH - (m.top+m.bottom);
42869             var totalWidth = (b.width + m.left + m.right);
42870             b.x = w - totalWidth + m.left;
42871             b.y = centerY + m.top;
42872             centerW -= totalWidth;
42873             east.updateBox(this.safeBox(b));
42874         }
42875         if(center){
42876             var m = center.getMargins();
42877             var centerBox = {
42878                 x: centerX + m.left,
42879                 y: centerY + m.top,
42880                 width: centerW - (m.left+m.right),
42881                 height: centerH - (m.top+m.bottom)
42882             };
42883             //if(this.hideOnLayout){
42884                 //center.el.setStyle("display", "block");
42885             //}
42886             center.updateBox(this.safeBox(centerBox));
42887         }
42888         this.el.repaint();
42889         this.fireEvent("layout", this);
42890     },
42891
42892     // private
42893     safeBox : function(box){
42894         box.width = Math.max(0, box.width);
42895         box.height = Math.max(0, box.height);
42896         return box;
42897     },
42898
42899     /**
42900      * Adds a ContentPanel (or subclass) to this layout.
42901      * @param {String} target The target region key (north, south, east, west or center).
42902      * @param {Roo.ContentPanel} panel The panel to add
42903      * @return {Roo.ContentPanel} The added panel
42904      */
42905     add : function(target, panel){
42906          
42907         target = target.toLowerCase();
42908         return this.regions[target].add(panel);
42909     },
42910
42911     /**
42912      * Remove a ContentPanel (or subclass) to this layout.
42913      * @param {String} target The target region key (north, south, east, west or center).
42914      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42915      * @return {Roo.ContentPanel} The removed panel
42916      */
42917     remove : function(target, panel){
42918         target = target.toLowerCase();
42919         return this.regions[target].remove(panel);
42920     },
42921
42922     /**
42923      * Searches all regions for a panel with the specified id
42924      * @param {String} panelId
42925      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42926      */
42927     findPanel : function(panelId){
42928         var rs = this.regions;
42929         for(var target in rs){
42930             if(typeof rs[target] != "function"){
42931                 var p = rs[target].getPanel(panelId);
42932                 if(p){
42933                     return p;
42934                 }
42935             }
42936         }
42937         return null;
42938     },
42939
42940     /**
42941      * Searches all regions for a panel with the specified id and activates (shows) it.
42942      * @param {String/ContentPanel} panelId The panels id or the panel itself
42943      * @return {Roo.ContentPanel} The shown panel or null
42944      */
42945     showPanel : function(panelId) {
42946       var rs = this.regions;
42947       for(var target in rs){
42948          var r = rs[target];
42949          if(typeof r != "function"){
42950             if(r.hasPanel(panelId)){
42951                return r.showPanel(panelId);
42952             }
42953          }
42954       }
42955       return null;
42956    },
42957
42958    /**
42959      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42960      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42961      */
42962    /*
42963     restoreState : function(provider){
42964         if(!provider){
42965             provider = Roo.state.Manager;
42966         }
42967         var sm = new Roo.LayoutStateManager();
42968         sm.init(this, provider);
42969     },
42970 */
42971  
42972  
42973     /**
42974      * Adds a xtype elements to the layout.
42975      * <pre><code>
42976
42977 layout.addxtype({
42978        xtype : 'ContentPanel',
42979        region: 'west',
42980        items: [ .... ]
42981    }
42982 );
42983
42984 layout.addxtype({
42985         xtype : 'NestedLayoutPanel',
42986         region: 'west',
42987         layout: {
42988            center: { },
42989            west: { }   
42990         },
42991         items : [ ... list of content panels or nested layout panels.. ]
42992    }
42993 );
42994 </code></pre>
42995      * @param {Object} cfg Xtype definition of item to add.
42996      */
42997     addxtype : function(cfg)
42998     {
42999         // basically accepts a pannel...
43000         // can accept a layout region..!?!?
43001         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43002         
43003         
43004         // theory?  children can only be panels??
43005         
43006         //if (!cfg.xtype.match(/Panel$/)) {
43007         //    return false;
43008         //}
43009         var ret = false;
43010         
43011         if (typeof(cfg.region) == 'undefined') {
43012             Roo.log("Failed to add Panel, region was not set");
43013             Roo.log(cfg);
43014             return false;
43015         }
43016         var region = cfg.region;
43017         delete cfg.region;
43018         
43019           
43020         var xitems = [];
43021         if (cfg.items) {
43022             xitems = cfg.items;
43023             delete cfg.items;
43024         }
43025         var nb = false;
43026         
43027         if ( region == 'center') {
43028             Roo.log("Center: " + cfg.title);
43029         }
43030         
43031         
43032         switch(cfg.xtype) 
43033         {
43034             case 'Content':  // ContentPanel (el, cfg)
43035             case 'Scroll':  // ContentPanel (el, cfg)
43036             case 'View': 
43037                 cfg.autoCreate = cfg.autoCreate || true;
43038                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43039                 //} else {
43040                 //    var el = this.el.createChild();
43041                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43042                 //}
43043                 
43044                 this.add(region, ret);
43045                 break;
43046             
43047             /*
43048             case 'TreePanel': // our new panel!
43049                 cfg.el = this.el.createChild();
43050                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43051                 this.add(region, ret);
43052                 break;
43053             */
43054             
43055             case 'Nest': 
43056                 // create a new Layout (which is  a Border Layout...
43057                 
43058                 var clayout = cfg.layout;
43059                 clayout.el  = this.el.createChild();
43060                 clayout.items   = clayout.items  || [];
43061                 
43062                 delete cfg.layout;
43063                 
43064                 // replace this exitems with the clayout ones..
43065                 xitems = clayout.items;
43066                  
43067                 // force background off if it's in center...
43068                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43069                     cfg.background = false;
43070                 }
43071                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
43072                 
43073                 
43074                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43075                 //console.log('adding nested layout panel '  + cfg.toSource());
43076                 this.add(region, ret);
43077                 nb = {}; /// find first...
43078                 break;
43079             
43080             case 'Grid':
43081                 
43082                 // needs grid and region
43083                 
43084                 //var el = this.getRegion(region).el.createChild();
43085                 /*
43086                  *var el = this.el.createChild();
43087                 // create the grid first...
43088                 cfg.grid.container = el;
43089                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43090                 */
43091                 
43092                 if (region == 'center' && this.active ) {
43093                     cfg.background = false;
43094                 }
43095                 
43096                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43097                 
43098                 this.add(region, ret);
43099                 /*
43100                 if (cfg.background) {
43101                     // render grid on panel activation (if panel background)
43102                     ret.on('activate', function(gp) {
43103                         if (!gp.grid.rendered) {
43104                     //        gp.grid.render(el);
43105                         }
43106                     });
43107                 } else {
43108                   //  cfg.grid.render(el);
43109                 }
43110                 */
43111                 break;
43112            
43113            
43114             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43115                 // it was the old xcomponent building that caused this before.
43116                 // espeically if border is the top element in the tree.
43117                 ret = this;
43118                 break; 
43119                 
43120                     
43121                 
43122                 
43123                 
43124             default:
43125                 /*
43126                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43127                     
43128                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43129                     this.add(region, ret);
43130                 } else {
43131                 */
43132                     Roo.log(cfg);
43133                     throw "Can not add '" + cfg.xtype + "' to Border";
43134                     return null;
43135              
43136                                 
43137              
43138         }
43139         this.beginUpdate();
43140         // add children..
43141         var region = '';
43142         var abn = {};
43143         Roo.each(xitems, function(i)  {
43144             region = nb && i.region ? i.region : false;
43145             
43146             var add = ret.addxtype(i);
43147            
43148             if (region) {
43149                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43150                 if (!i.background) {
43151                     abn[region] = nb[region] ;
43152                 }
43153             }
43154             
43155         });
43156         this.endUpdate();
43157
43158         // make the last non-background panel active..
43159         //if (nb) { Roo.log(abn); }
43160         if (nb) {
43161             
43162             for(var r in abn) {
43163                 region = this.getRegion(r);
43164                 if (region) {
43165                     // tried using nb[r], but it does not work..
43166                      
43167                     region.showPanel(abn[r]);
43168                    
43169                 }
43170             }
43171         }
43172         return ret;
43173         
43174     },
43175     
43176     
43177 // private
43178     factory : function(cfg)
43179     {
43180         
43181         var validRegions = Roo.bootstrap.layout.Border.regions;
43182
43183         var target = cfg.region;
43184         cfg.mgr = this;
43185         
43186         var r = Roo.bootstrap.layout;
43187         Roo.log(target);
43188         switch(target){
43189             case "north":
43190                 return new r.North(cfg);
43191             case "south":
43192                 return new r.South(cfg);
43193             case "east":
43194                 return new r.East(cfg);
43195             case "west":
43196                 return new r.West(cfg);
43197             case "center":
43198                 return new r.Center(cfg);
43199         }
43200         throw 'Layout region "'+target+'" not supported.';
43201     }
43202     
43203     
43204 });
43205  /*
43206  * Based on:
43207  * Ext JS Library 1.1.1
43208  * Copyright(c) 2006-2007, Ext JS, LLC.
43209  *
43210  * Originally Released Under LGPL - original licence link has changed is not relivant.
43211  *
43212  * Fork - LGPL
43213  * <script type="text/javascript">
43214  */
43215  
43216 /**
43217  * @class Roo.bootstrap.layout.Basic
43218  * @extends Roo.util.Observable
43219  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43220  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43221  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43222  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43223  * @cfg {string}   region  the region that it inhabits..
43224  * @cfg {bool}   skipConfig skip config?
43225  * 
43226
43227  */
43228 Roo.bootstrap.layout.Basic = function(config){
43229     
43230     this.mgr = config.mgr;
43231     
43232     this.position = config.region;
43233     
43234     var skipConfig = config.skipConfig;
43235     
43236     this.events = {
43237         /**
43238          * @scope Roo.BasicLayoutRegion
43239          */
43240         
43241         /**
43242          * @event beforeremove
43243          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43244          * @param {Roo.LayoutRegion} this
43245          * @param {Roo.ContentPanel} panel The panel
43246          * @param {Object} e The cancel event object
43247          */
43248         "beforeremove" : true,
43249         /**
43250          * @event invalidated
43251          * Fires when the layout for this region is changed.
43252          * @param {Roo.LayoutRegion} this
43253          */
43254         "invalidated" : true,
43255         /**
43256          * @event visibilitychange
43257          * Fires when this region is shown or hidden 
43258          * @param {Roo.LayoutRegion} this
43259          * @param {Boolean} visibility true or false
43260          */
43261         "visibilitychange" : true,
43262         /**
43263          * @event paneladded
43264          * Fires when a panel is added. 
43265          * @param {Roo.LayoutRegion} this
43266          * @param {Roo.ContentPanel} panel The panel
43267          */
43268         "paneladded" : true,
43269         /**
43270          * @event panelremoved
43271          * Fires when a panel is removed. 
43272          * @param {Roo.LayoutRegion} this
43273          * @param {Roo.ContentPanel} panel The panel
43274          */
43275         "panelremoved" : true,
43276         /**
43277          * @event beforecollapse
43278          * Fires when this region before collapse.
43279          * @param {Roo.LayoutRegion} this
43280          */
43281         "beforecollapse" : true,
43282         /**
43283          * @event collapsed
43284          * Fires when this region is collapsed.
43285          * @param {Roo.LayoutRegion} this
43286          */
43287         "collapsed" : true,
43288         /**
43289          * @event expanded
43290          * Fires when this region is expanded.
43291          * @param {Roo.LayoutRegion} this
43292          */
43293         "expanded" : true,
43294         /**
43295          * @event slideshow
43296          * Fires when this region is slid into view.
43297          * @param {Roo.LayoutRegion} this
43298          */
43299         "slideshow" : true,
43300         /**
43301          * @event slidehide
43302          * Fires when this region slides out of view. 
43303          * @param {Roo.LayoutRegion} this
43304          */
43305         "slidehide" : true,
43306         /**
43307          * @event panelactivated
43308          * Fires when a panel is activated. 
43309          * @param {Roo.LayoutRegion} this
43310          * @param {Roo.ContentPanel} panel The activated panel
43311          */
43312         "panelactivated" : true,
43313         /**
43314          * @event resized
43315          * Fires when the user resizes this region. 
43316          * @param {Roo.LayoutRegion} this
43317          * @param {Number} newSize The new size (width for east/west, height for north/south)
43318          */
43319         "resized" : true
43320     };
43321     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43322     this.panels = new Roo.util.MixedCollection();
43323     this.panels.getKey = this.getPanelId.createDelegate(this);
43324     this.box = null;
43325     this.activePanel = null;
43326     // ensure listeners are added...
43327     
43328     if (config.listeners || config.events) {
43329         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43330             listeners : config.listeners || {},
43331             events : config.events || {}
43332         });
43333     }
43334     
43335     if(skipConfig !== true){
43336         this.applyConfig(config);
43337     }
43338 };
43339
43340 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43341 {
43342     getPanelId : function(p){
43343         return p.getId();
43344     },
43345     
43346     applyConfig : function(config){
43347         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43348         this.config = config;
43349         
43350     },
43351     
43352     /**
43353      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43354      * the width, for horizontal (north, south) the height.
43355      * @param {Number} newSize The new width or height
43356      */
43357     resizeTo : function(newSize){
43358         var el = this.el ? this.el :
43359                  (this.activePanel ? this.activePanel.getEl() : null);
43360         if(el){
43361             switch(this.position){
43362                 case "east":
43363                 case "west":
43364                     el.setWidth(newSize);
43365                     this.fireEvent("resized", this, newSize);
43366                 break;
43367                 case "north":
43368                 case "south":
43369                     el.setHeight(newSize);
43370                     this.fireEvent("resized", this, newSize);
43371                 break;                
43372             }
43373         }
43374     },
43375     
43376     getBox : function(){
43377         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43378     },
43379     
43380     getMargins : function(){
43381         return this.margins;
43382     },
43383     
43384     updateBox : function(box){
43385         this.box = box;
43386         var el = this.activePanel.getEl();
43387         el.dom.style.left = box.x + "px";
43388         el.dom.style.top = box.y + "px";
43389         this.activePanel.setSize(box.width, box.height);
43390     },
43391     
43392     /**
43393      * Returns the container element for this region.
43394      * @return {Roo.Element}
43395      */
43396     getEl : function(){
43397         return this.activePanel;
43398     },
43399     
43400     /**
43401      * Returns true if this region is currently visible.
43402      * @return {Boolean}
43403      */
43404     isVisible : function(){
43405         return this.activePanel ? true : false;
43406     },
43407     
43408     setActivePanel : function(panel){
43409         panel = this.getPanel(panel);
43410         if(this.activePanel && this.activePanel != panel){
43411             this.activePanel.setActiveState(false);
43412             this.activePanel.getEl().setLeftTop(-10000,-10000);
43413         }
43414         this.activePanel = panel;
43415         panel.setActiveState(true);
43416         if(this.box){
43417             panel.setSize(this.box.width, this.box.height);
43418         }
43419         this.fireEvent("panelactivated", this, panel);
43420         this.fireEvent("invalidated");
43421     },
43422     
43423     /**
43424      * Show the specified panel.
43425      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43426      * @return {Roo.ContentPanel} The shown panel or null
43427      */
43428     showPanel : function(panel){
43429         panel = this.getPanel(panel);
43430         if(panel){
43431             this.setActivePanel(panel);
43432         }
43433         return panel;
43434     },
43435     
43436     /**
43437      * Get the active panel for this region.
43438      * @return {Roo.ContentPanel} The active panel or null
43439      */
43440     getActivePanel : function(){
43441         return this.activePanel;
43442     },
43443     
43444     /**
43445      * Add the passed ContentPanel(s)
43446      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43447      * @return {Roo.ContentPanel} The panel added (if only one was added)
43448      */
43449     add : function(panel){
43450         if(arguments.length > 1){
43451             for(var i = 0, len = arguments.length; i < len; i++) {
43452                 this.add(arguments[i]);
43453             }
43454             return null;
43455         }
43456         if(this.hasPanel(panel)){
43457             this.showPanel(panel);
43458             return panel;
43459         }
43460         var el = panel.getEl();
43461         if(el.dom.parentNode != this.mgr.el.dom){
43462             this.mgr.el.dom.appendChild(el.dom);
43463         }
43464         if(panel.setRegion){
43465             panel.setRegion(this);
43466         }
43467         this.panels.add(panel);
43468         el.setStyle("position", "absolute");
43469         if(!panel.background){
43470             this.setActivePanel(panel);
43471             if(this.config.initialSize && this.panels.getCount()==1){
43472                 this.resizeTo(this.config.initialSize);
43473             }
43474         }
43475         this.fireEvent("paneladded", this, panel);
43476         return panel;
43477     },
43478     
43479     /**
43480      * Returns true if the panel is in this region.
43481      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43482      * @return {Boolean}
43483      */
43484     hasPanel : function(panel){
43485         if(typeof panel == "object"){ // must be panel obj
43486             panel = panel.getId();
43487         }
43488         return this.getPanel(panel) ? true : false;
43489     },
43490     
43491     /**
43492      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43493      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43494      * @param {Boolean} preservePanel Overrides the config preservePanel option
43495      * @return {Roo.ContentPanel} The panel that was removed
43496      */
43497     remove : function(panel, preservePanel){
43498         panel = this.getPanel(panel);
43499         if(!panel){
43500             return null;
43501         }
43502         var e = {};
43503         this.fireEvent("beforeremove", this, panel, e);
43504         if(e.cancel === true){
43505             return null;
43506         }
43507         var panelId = panel.getId();
43508         this.panels.removeKey(panelId);
43509         return panel;
43510     },
43511     
43512     /**
43513      * Returns the panel specified or null if it's not in this region.
43514      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43515      * @return {Roo.ContentPanel}
43516      */
43517     getPanel : function(id){
43518         if(typeof id == "object"){ // must be panel obj
43519             return id;
43520         }
43521         return this.panels.get(id);
43522     },
43523     
43524     /**
43525      * Returns this regions position (north/south/east/west/center).
43526      * @return {String} 
43527      */
43528     getPosition: function(){
43529         return this.position;    
43530     }
43531 });/*
43532  * Based on:
43533  * Ext JS Library 1.1.1
43534  * Copyright(c) 2006-2007, Ext JS, LLC.
43535  *
43536  * Originally Released Under LGPL - original licence link has changed is not relivant.
43537  *
43538  * Fork - LGPL
43539  * <script type="text/javascript">
43540  */
43541  
43542 /**
43543  * @class Roo.bootstrap.layout.Region
43544  * @extends Roo.bootstrap.layout.Basic
43545  * This class represents a region in a layout manager.
43546  
43547  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43548  * @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})
43549  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43550  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43551  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43552  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43553  * @cfg {String}    title           The title for the region (overrides panel titles)
43554  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43555  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43556  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43557  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43558  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43559  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43560  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43561  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43562  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43563  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43564
43565  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43566  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43567  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43568  * @cfg {Number}    width           For East/West panels
43569  * @cfg {Number}    height          For North/South panels
43570  * @cfg {Boolean}   split           To show the splitter
43571  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43572  * 
43573  * @cfg {string}   cls             Extra CSS classes to add to region
43574  * 
43575  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43576  * @cfg {string}   region  the region that it inhabits..
43577  *
43578
43579  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43580  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43581
43582  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43583  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43584  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43585  */
43586 Roo.bootstrap.layout.Region = function(config)
43587 {
43588     this.applyConfig(config);
43589
43590     var mgr = config.mgr;
43591     var pos = config.region;
43592     config.skipConfig = true;
43593     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43594     
43595     if (mgr.el) {
43596         this.onRender(mgr.el);   
43597     }
43598      
43599     this.visible = true;
43600     this.collapsed = false;
43601     this.unrendered_panels = [];
43602 };
43603
43604 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43605
43606     position: '', // set by wrapper (eg. north/south etc..)
43607     unrendered_panels : null,  // unrendered panels.
43608     
43609     tabPosition : false,
43610     
43611     mgr: false, // points to 'Border'
43612     
43613     
43614     createBody : function(){
43615         /** This region's body element 
43616         * @type Roo.Element */
43617         this.bodyEl = this.el.createChild({
43618                 tag: "div",
43619                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43620         });
43621     },
43622
43623     onRender: function(ctr, pos)
43624     {
43625         var dh = Roo.DomHelper;
43626         /** This region's container element 
43627         * @type Roo.Element */
43628         this.el = dh.append(ctr.dom, {
43629                 tag: "div",
43630                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43631             }, true);
43632         /** This region's title element 
43633         * @type Roo.Element */
43634     
43635         this.titleEl = dh.append(this.el.dom,  {
43636                 tag: "div",
43637                 unselectable: "on",
43638                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43639                 children:[
43640                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43641                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43642                 ]
43643             }, true);
43644         
43645         this.titleEl.enableDisplayMode();
43646         /** This region's title text element 
43647         * @type HTMLElement */
43648         this.titleTextEl = this.titleEl.dom.firstChild;
43649         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43650         /*
43651         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43652         this.closeBtn.enableDisplayMode();
43653         this.closeBtn.on("click", this.closeClicked, this);
43654         this.closeBtn.hide();
43655     */
43656         this.createBody(this.config);
43657         if(this.config.hideWhenEmpty){
43658             this.hide();
43659             this.on("paneladded", this.validateVisibility, this);
43660             this.on("panelremoved", this.validateVisibility, this);
43661         }
43662         if(this.autoScroll){
43663             this.bodyEl.setStyle("overflow", "auto");
43664         }else{
43665             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43666         }
43667         //if(c.titlebar !== false){
43668             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43669                 this.titleEl.hide();
43670             }else{
43671                 this.titleEl.show();
43672                 if(this.config.title){
43673                     this.titleTextEl.innerHTML = this.config.title;
43674                 }
43675             }
43676         //}
43677         if(this.config.collapsed){
43678             this.collapse(true);
43679         }
43680         if(this.config.hidden){
43681             this.hide();
43682         }
43683         
43684         if (this.unrendered_panels && this.unrendered_panels.length) {
43685             for (var i =0;i< this.unrendered_panels.length; i++) {
43686                 this.add(this.unrendered_panels[i]);
43687             }
43688             this.unrendered_panels = null;
43689             
43690         }
43691         
43692     },
43693     
43694     applyConfig : function(c)
43695     {
43696         /*
43697          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43698             var dh = Roo.DomHelper;
43699             if(c.titlebar !== false){
43700                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43701                 this.collapseBtn.on("click", this.collapse, this);
43702                 this.collapseBtn.enableDisplayMode();
43703                 /*
43704                 if(c.showPin === true || this.showPin){
43705                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43706                     this.stickBtn.enableDisplayMode();
43707                     this.stickBtn.on("click", this.expand, this);
43708                     this.stickBtn.hide();
43709                 }
43710                 
43711             }
43712             */
43713             /** This region's collapsed element
43714             * @type Roo.Element */
43715             /*
43716              *
43717             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43718                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43719             ]}, true);
43720             
43721             if(c.floatable !== false){
43722                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43723                this.collapsedEl.on("click", this.collapseClick, this);
43724             }
43725
43726             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43727                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43728                    id: "message", unselectable: "on", style:{"float":"left"}});
43729                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43730              }
43731             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43732             this.expandBtn.on("click", this.expand, this);
43733             
43734         }
43735         
43736         if(this.collapseBtn){
43737             this.collapseBtn.setVisible(c.collapsible == true);
43738         }
43739         
43740         this.cmargins = c.cmargins || this.cmargins ||
43741                          (this.position == "west" || this.position == "east" ?
43742                              {top: 0, left: 2, right:2, bottom: 0} :
43743                              {top: 2, left: 0, right:0, bottom: 2});
43744         */
43745         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43746         
43747         
43748         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43749         
43750         this.autoScroll = c.autoScroll || false;
43751         
43752         
43753        
43754         
43755         this.duration = c.duration || .30;
43756         this.slideDuration = c.slideDuration || .45;
43757         this.config = c;
43758        
43759     },
43760     /**
43761      * Returns true if this region is currently visible.
43762      * @return {Boolean}
43763      */
43764     isVisible : function(){
43765         return this.visible;
43766     },
43767
43768     /**
43769      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43770      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43771      */
43772     //setCollapsedTitle : function(title){
43773     //    title = title || "&#160;";
43774      //   if(this.collapsedTitleTextEl){
43775       //      this.collapsedTitleTextEl.innerHTML = title;
43776        // }
43777     //},
43778
43779     getBox : function(){
43780         var b;
43781       //  if(!this.collapsed){
43782             b = this.el.getBox(false, true);
43783        // }else{
43784           //  b = this.collapsedEl.getBox(false, true);
43785         //}
43786         return b;
43787     },
43788
43789     getMargins : function(){
43790         return this.margins;
43791         //return this.collapsed ? this.cmargins : this.margins;
43792     },
43793 /*
43794     highlight : function(){
43795         this.el.addClass("x-layout-panel-dragover");
43796     },
43797
43798     unhighlight : function(){
43799         this.el.removeClass("x-layout-panel-dragover");
43800     },
43801 */
43802     updateBox : function(box)
43803     {
43804         if (!this.bodyEl) {
43805             return; // not rendered yet..
43806         }
43807         
43808         this.box = box;
43809         if(!this.collapsed){
43810             this.el.dom.style.left = box.x + "px";
43811             this.el.dom.style.top = box.y + "px";
43812             this.updateBody(box.width, box.height);
43813         }else{
43814             this.collapsedEl.dom.style.left = box.x + "px";
43815             this.collapsedEl.dom.style.top = box.y + "px";
43816             this.collapsedEl.setSize(box.width, box.height);
43817         }
43818         if(this.tabs){
43819             this.tabs.autoSizeTabs();
43820         }
43821     },
43822
43823     updateBody : function(w, h)
43824     {
43825         if(w !== null){
43826             this.el.setWidth(w);
43827             w -= this.el.getBorderWidth("rl");
43828             if(this.config.adjustments){
43829                 w += this.config.adjustments[0];
43830             }
43831         }
43832         if(h !== null && h > 0){
43833             this.el.setHeight(h);
43834             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43835             h -= this.el.getBorderWidth("tb");
43836             if(this.config.adjustments){
43837                 h += this.config.adjustments[1];
43838             }
43839             this.bodyEl.setHeight(h);
43840             if(this.tabs){
43841                 h = this.tabs.syncHeight(h);
43842             }
43843         }
43844         if(this.panelSize){
43845             w = w !== null ? w : this.panelSize.width;
43846             h = h !== null ? h : this.panelSize.height;
43847         }
43848         if(this.activePanel){
43849             var el = this.activePanel.getEl();
43850             w = w !== null ? w : el.getWidth();
43851             h = h !== null ? h : el.getHeight();
43852             this.panelSize = {width: w, height: h};
43853             this.activePanel.setSize(w, h);
43854         }
43855         if(Roo.isIE && this.tabs){
43856             this.tabs.el.repaint();
43857         }
43858     },
43859
43860     /**
43861      * Returns the container element for this region.
43862      * @return {Roo.Element}
43863      */
43864     getEl : function(){
43865         return this.el;
43866     },
43867
43868     /**
43869      * Hides this region.
43870      */
43871     hide : function(){
43872         //if(!this.collapsed){
43873             this.el.dom.style.left = "-2000px";
43874             this.el.hide();
43875         //}else{
43876          //   this.collapsedEl.dom.style.left = "-2000px";
43877          //   this.collapsedEl.hide();
43878        // }
43879         this.visible = false;
43880         this.fireEvent("visibilitychange", this, false);
43881     },
43882
43883     /**
43884      * Shows this region if it was previously hidden.
43885      */
43886     show : function(){
43887         //if(!this.collapsed){
43888             this.el.show();
43889         //}else{
43890         //    this.collapsedEl.show();
43891        // }
43892         this.visible = true;
43893         this.fireEvent("visibilitychange", this, true);
43894     },
43895 /*
43896     closeClicked : function(){
43897         if(this.activePanel){
43898             this.remove(this.activePanel);
43899         }
43900     },
43901
43902     collapseClick : function(e){
43903         if(this.isSlid){
43904            e.stopPropagation();
43905            this.slideIn();
43906         }else{
43907            e.stopPropagation();
43908            this.slideOut();
43909         }
43910     },
43911 */
43912     /**
43913      * Collapses this region.
43914      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43915      */
43916     /*
43917     collapse : function(skipAnim, skipCheck = false){
43918         if(this.collapsed) {
43919             return;
43920         }
43921         
43922         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43923             
43924             this.collapsed = true;
43925             if(this.split){
43926                 this.split.el.hide();
43927             }
43928             if(this.config.animate && skipAnim !== true){
43929                 this.fireEvent("invalidated", this);
43930                 this.animateCollapse();
43931             }else{
43932                 this.el.setLocation(-20000,-20000);
43933                 this.el.hide();
43934                 this.collapsedEl.show();
43935                 this.fireEvent("collapsed", this);
43936                 this.fireEvent("invalidated", this);
43937             }
43938         }
43939         
43940     },
43941 */
43942     animateCollapse : function(){
43943         // overridden
43944     },
43945
43946     /**
43947      * Expands this region if it was previously collapsed.
43948      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43949      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43950      */
43951     /*
43952     expand : function(e, skipAnim){
43953         if(e) {
43954             e.stopPropagation();
43955         }
43956         if(!this.collapsed || this.el.hasActiveFx()) {
43957             return;
43958         }
43959         if(this.isSlid){
43960             this.afterSlideIn();
43961             skipAnim = true;
43962         }
43963         this.collapsed = false;
43964         if(this.config.animate && skipAnim !== true){
43965             this.animateExpand();
43966         }else{
43967             this.el.show();
43968             if(this.split){
43969                 this.split.el.show();
43970             }
43971             this.collapsedEl.setLocation(-2000,-2000);
43972             this.collapsedEl.hide();
43973             this.fireEvent("invalidated", this);
43974             this.fireEvent("expanded", this);
43975         }
43976     },
43977 */
43978     animateExpand : function(){
43979         // overridden
43980     },
43981
43982     initTabs : function()
43983     {
43984         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
43985         
43986         var ts = new Roo.bootstrap.panel.Tabs({
43987             el: this.bodyEl.dom,
43988             region : this,
43989             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
43990             disableTooltips: this.config.disableTabTips,
43991             toolbar : this.config.toolbar
43992         });
43993         
43994         if(this.config.hideTabs){
43995             ts.stripWrap.setDisplayed(false);
43996         }
43997         this.tabs = ts;
43998         ts.resizeTabs = this.config.resizeTabs === true;
43999         ts.minTabWidth = this.config.minTabWidth || 40;
44000         ts.maxTabWidth = this.config.maxTabWidth || 250;
44001         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44002         ts.monitorResize = false;
44003         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44004         ts.bodyEl.addClass('roo-layout-tabs-body');
44005         this.panels.each(this.initPanelAsTab, this);
44006     },
44007
44008     initPanelAsTab : function(panel){
44009         var ti = this.tabs.addTab(
44010             panel.getEl().id,
44011             panel.getTitle(),
44012             null,
44013             this.config.closeOnTab && panel.isClosable(),
44014             panel.tpl
44015         );
44016         if(panel.tabTip !== undefined){
44017             ti.setTooltip(panel.tabTip);
44018         }
44019         ti.on("activate", function(){
44020               this.setActivePanel(panel);
44021         }, this);
44022         
44023         if(this.config.closeOnTab){
44024             ti.on("beforeclose", function(t, e){
44025                 e.cancel = true;
44026                 this.remove(panel);
44027             }, this);
44028         }
44029         
44030         panel.tabItem = ti;
44031         
44032         return ti;
44033     },
44034
44035     updatePanelTitle : function(panel, title)
44036     {
44037         if(this.activePanel == panel){
44038             this.updateTitle(title);
44039         }
44040         if(this.tabs){
44041             var ti = this.tabs.getTab(panel.getEl().id);
44042             ti.setText(title);
44043             if(panel.tabTip !== undefined){
44044                 ti.setTooltip(panel.tabTip);
44045             }
44046         }
44047     },
44048
44049     updateTitle : function(title){
44050         if(this.titleTextEl && !this.config.title){
44051             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44052         }
44053     },
44054
44055     setActivePanel : function(panel)
44056     {
44057         panel = this.getPanel(panel);
44058         if(this.activePanel && this.activePanel != panel){
44059             if(this.activePanel.setActiveState(false) === false){
44060                 return;
44061             }
44062         }
44063         this.activePanel = panel;
44064         panel.setActiveState(true);
44065         if(this.panelSize){
44066             panel.setSize(this.panelSize.width, this.panelSize.height);
44067         }
44068         if(this.closeBtn){
44069             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44070         }
44071         this.updateTitle(panel.getTitle());
44072         if(this.tabs){
44073             this.fireEvent("invalidated", this);
44074         }
44075         this.fireEvent("panelactivated", this, panel);
44076     },
44077
44078     /**
44079      * Shows the specified panel.
44080      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44081      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44082      */
44083     showPanel : function(panel)
44084     {
44085         panel = this.getPanel(panel);
44086         if(panel){
44087             if(this.tabs){
44088                 var tab = this.tabs.getTab(panel.getEl().id);
44089                 if(tab.isHidden()){
44090                     this.tabs.unhideTab(tab.id);
44091                 }
44092                 tab.activate();
44093             }else{
44094                 this.setActivePanel(panel);
44095             }
44096         }
44097         return panel;
44098     },
44099
44100     /**
44101      * Get the active panel for this region.
44102      * @return {Roo.ContentPanel} The active panel or null
44103      */
44104     getActivePanel : function(){
44105         return this.activePanel;
44106     },
44107
44108     validateVisibility : function(){
44109         if(this.panels.getCount() < 1){
44110             this.updateTitle("&#160;");
44111             this.closeBtn.hide();
44112             this.hide();
44113         }else{
44114             if(!this.isVisible()){
44115                 this.show();
44116             }
44117         }
44118     },
44119
44120     /**
44121      * Adds the passed ContentPanel(s) to this region.
44122      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44123      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44124      */
44125     add : function(panel)
44126     {
44127         if(arguments.length > 1){
44128             for(var i = 0, len = arguments.length; i < len; i++) {
44129                 this.add(arguments[i]);
44130             }
44131             return null;
44132         }
44133         
44134         // if we have not been rendered yet, then we can not really do much of this..
44135         if (!this.bodyEl) {
44136             this.unrendered_panels.push(panel);
44137             return panel;
44138         }
44139         
44140         
44141         
44142         
44143         if(this.hasPanel(panel)){
44144             this.showPanel(panel);
44145             return panel;
44146         }
44147         panel.setRegion(this);
44148         this.panels.add(panel);
44149        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44150             // sinle panel - no tab...?? would it not be better to render it with the tabs,
44151             // and hide them... ???
44152             this.bodyEl.dom.appendChild(panel.getEl().dom);
44153             if(panel.background !== true){
44154                 this.setActivePanel(panel);
44155             }
44156             this.fireEvent("paneladded", this, panel);
44157             return panel;
44158         }
44159         */
44160         if(!this.tabs){
44161             this.initTabs();
44162         }else{
44163             this.initPanelAsTab(panel);
44164         }
44165         
44166         
44167         if(panel.background !== true){
44168             this.tabs.activate(panel.getEl().id);
44169         }
44170         this.fireEvent("paneladded", this, panel);
44171         return panel;
44172     },
44173
44174     /**
44175      * Hides the tab for the specified panel.
44176      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44177      */
44178     hidePanel : function(panel){
44179         if(this.tabs && (panel = this.getPanel(panel))){
44180             this.tabs.hideTab(panel.getEl().id);
44181         }
44182     },
44183
44184     /**
44185      * Unhides the tab for a previously hidden panel.
44186      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44187      */
44188     unhidePanel : function(panel){
44189         if(this.tabs && (panel = this.getPanel(panel))){
44190             this.tabs.unhideTab(panel.getEl().id);
44191         }
44192     },
44193
44194     clearPanels : function(){
44195         while(this.panels.getCount() > 0){
44196              this.remove(this.panels.first());
44197         }
44198     },
44199
44200     /**
44201      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44202      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44203      * @param {Boolean} preservePanel Overrides the config preservePanel option
44204      * @return {Roo.ContentPanel} The panel that was removed
44205      */
44206     remove : function(panel, preservePanel)
44207     {
44208         panel = this.getPanel(panel);
44209         if(!panel){
44210             return null;
44211         }
44212         var e = {};
44213         this.fireEvent("beforeremove", this, panel, e);
44214         if(e.cancel === true){
44215             return null;
44216         }
44217         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44218         var panelId = panel.getId();
44219         this.panels.removeKey(panelId);
44220         if(preservePanel){
44221             document.body.appendChild(panel.getEl().dom);
44222         }
44223         if(this.tabs){
44224             this.tabs.removeTab(panel.getEl().id);
44225         }else if (!preservePanel){
44226             this.bodyEl.dom.removeChild(panel.getEl().dom);
44227         }
44228         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44229             var p = this.panels.first();
44230             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44231             tempEl.appendChild(p.getEl().dom);
44232             this.bodyEl.update("");
44233             this.bodyEl.dom.appendChild(p.getEl().dom);
44234             tempEl = null;
44235             this.updateTitle(p.getTitle());
44236             this.tabs = null;
44237             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44238             this.setActivePanel(p);
44239         }
44240         panel.setRegion(null);
44241         if(this.activePanel == panel){
44242             this.activePanel = null;
44243         }
44244         if(this.config.autoDestroy !== false && preservePanel !== true){
44245             try{panel.destroy();}catch(e){}
44246         }
44247         this.fireEvent("panelremoved", this, panel);
44248         return panel;
44249     },
44250
44251     /**
44252      * Returns the TabPanel component used by this region
44253      * @return {Roo.TabPanel}
44254      */
44255     getTabs : function(){
44256         return this.tabs;
44257     },
44258
44259     createTool : function(parentEl, className){
44260         var btn = Roo.DomHelper.append(parentEl, {
44261             tag: "div",
44262             cls: "x-layout-tools-button",
44263             children: [ {
44264                 tag: "div",
44265                 cls: "roo-layout-tools-button-inner " + className,
44266                 html: "&#160;"
44267             }]
44268         }, true);
44269         btn.addClassOnOver("roo-layout-tools-button-over");
44270         return btn;
44271     }
44272 });/*
44273  * Based on:
44274  * Ext JS Library 1.1.1
44275  * Copyright(c) 2006-2007, Ext JS, LLC.
44276  *
44277  * Originally Released Under LGPL - original licence link has changed is not relivant.
44278  *
44279  * Fork - LGPL
44280  * <script type="text/javascript">
44281  */
44282  
44283
44284
44285 /**
44286  * @class Roo.SplitLayoutRegion
44287  * @extends Roo.LayoutRegion
44288  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44289  */
44290 Roo.bootstrap.layout.Split = function(config){
44291     this.cursor = config.cursor;
44292     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44293 };
44294
44295 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44296 {
44297     splitTip : "Drag to resize.",
44298     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44299     useSplitTips : false,
44300
44301     applyConfig : function(config){
44302         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44303     },
44304     
44305     onRender : function(ctr,pos) {
44306         
44307         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44308         if(!this.config.split){
44309             return;
44310         }
44311         if(!this.split){
44312             
44313             var splitEl = Roo.DomHelper.append(ctr.dom,  {
44314                             tag: "div",
44315                             id: this.el.id + "-split",
44316                             cls: "roo-layout-split roo-layout-split-"+this.position,
44317                             html: "&#160;"
44318             });
44319             /** The SplitBar for this region 
44320             * @type Roo.SplitBar */
44321             // does not exist yet...
44322             Roo.log([this.position, this.orientation]);
44323             
44324             this.split = new Roo.bootstrap.SplitBar({
44325                 dragElement : splitEl,
44326                 resizingElement: this.el,
44327                 orientation : this.orientation
44328             });
44329             
44330             this.split.on("moved", this.onSplitMove, this);
44331             this.split.useShim = this.config.useShim === true;
44332             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44333             if(this.useSplitTips){
44334                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44335             }
44336             //if(config.collapsible){
44337             //    this.split.el.on("dblclick", this.collapse,  this);
44338             //}
44339         }
44340         if(typeof this.config.minSize != "undefined"){
44341             this.split.minSize = this.config.minSize;
44342         }
44343         if(typeof this.config.maxSize != "undefined"){
44344             this.split.maxSize = this.config.maxSize;
44345         }
44346         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44347             this.hideSplitter();
44348         }
44349         
44350     },
44351
44352     getHMaxSize : function(){
44353          var cmax = this.config.maxSize || 10000;
44354          var center = this.mgr.getRegion("center");
44355          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44356     },
44357
44358     getVMaxSize : function(){
44359          var cmax = this.config.maxSize || 10000;
44360          var center = this.mgr.getRegion("center");
44361          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44362     },
44363
44364     onSplitMove : function(split, newSize){
44365         this.fireEvent("resized", this, newSize);
44366     },
44367     
44368     /** 
44369      * Returns the {@link Roo.SplitBar} for this region.
44370      * @return {Roo.SplitBar}
44371      */
44372     getSplitBar : function(){
44373         return this.split;
44374     },
44375     
44376     hide : function(){
44377         this.hideSplitter();
44378         Roo.bootstrap.layout.Split.superclass.hide.call(this);
44379     },
44380
44381     hideSplitter : function(){
44382         if(this.split){
44383             this.split.el.setLocation(-2000,-2000);
44384             this.split.el.hide();
44385         }
44386     },
44387
44388     show : function(){
44389         if(this.split){
44390             this.split.el.show();
44391         }
44392         Roo.bootstrap.layout.Split.superclass.show.call(this);
44393     },
44394     
44395     beforeSlide: function(){
44396         if(Roo.isGecko){// firefox overflow auto bug workaround
44397             this.bodyEl.clip();
44398             if(this.tabs) {
44399                 this.tabs.bodyEl.clip();
44400             }
44401             if(this.activePanel){
44402                 this.activePanel.getEl().clip();
44403                 
44404                 if(this.activePanel.beforeSlide){
44405                     this.activePanel.beforeSlide();
44406                 }
44407             }
44408         }
44409     },
44410     
44411     afterSlide : function(){
44412         if(Roo.isGecko){// firefox overflow auto bug workaround
44413             this.bodyEl.unclip();
44414             if(this.tabs) {
44415                 this.tabs.bodyEl.unclip();
44416             }
44417             if(this.activePanel){
44418                 this.activePanel.getEl().unclip();
44419                 if(this.activePanel.afterSlide){
44420                     this.activePanel.afterSlide();
44421                 }
44422             }
44423         }
44424     },
44425
44426     initAutoHide : function(){
44427         if(this.autoHide !== false){
44428             if(!this.autoHideHd){
44429                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44430                 this.autoHideHd = {
44431                     "mouseout": function(e){
44432                         if(!e.within(this.el, true)){
44433                             st.delay(500);
44434                         }
44435                     },
44436                     "mouseover" : function(e){
44437                         st.cancel();
44438                     },
44439                     scope : this
44440                 };
44441             }
44442             this.el.on(this.autoHideHd);
44443         }
44444     },
44445
44446     clearAutoHide : function(){
44447         if(this.autoHide !== false){
44448             this.el.un("mouseout", this.autoHideHd.mouseout);
44449             this.el.un("mouseover", this.autoHideHd.mouseover);
44450         }
44451     },
44452
44453     clearMonitor : function(){
44454         Roo.get(document).un("click", this.slideInIf, this);
44455     },
44456
44457     // these names are backwards but not changed for compat
44458     slideOut : function(){
44459         if(this.isSlid || this.el.hasActiveFx()){
44460             return;
44461         }
44462         this.isSlid = true;
44463         if(this.collapseBtn){
44464             this.collapseBtn.hide();
44465         }
44466         this.closeBtnState = this.closeBtn.getStyle('display');
44467         this.closeBtn.hide();
44468         if(this.stickBtn){
44469             this.stickBtn.show();
44470         }
44471         this.el.show();
44472         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44473         this.beforeSlide();
44474         this.el.setStyle("z-index", 10001);
44475         this.el.slideIn(this.getSlideAnchor(), {
44476             callback: function(){
44477                 this.afterSlide();
44478                 this.initAutoHide();
44479                 Roo.get(document).on("click", this.slideInIf, this);
44480                 this.fireEvent("slideshow", this);
44481             },
44482             scope: this,
44483             block: true
44484         });
44485     },
44486
44487     afterSlideIn : function(){
44488         this.clearAutoHide();
44489         this.isSlid = false;
44490         this.clearMonitor();
44491         this.el.setStyle("z-index", "");
44492         if(this.collapseBtn){
44493             this.collapseBtn.show();
44494         }
44495         this.closeBtn.setStyle('display', this.closeBtnState);
44496         if(this.stickBtn){
44497             this.stickBtn.hide();
44498         }
44499         this.fireEvent("slidehide", this);
44500     },
44501
44502     slideIn : function(cb){
44503         if(!this.isSlid || this.el.hasActiveFx()){
44504             Roo.callback(cb);
44505             return;
44506         }
44507         this.isSlid = false;
44508         this.beforeSlide();
44509         this.el.slideOut(this.getSlideAnchor(), {
44510             callback: function(){
44511                 this.el.setLeftTop(-10000, -10000);
44512                 this.afterSlide();
44513                 this.afterSlideIn();
44514                 Roo.callback(cb);
44515             },
44516             scope: this,
44517             block: true
44518         });
44519     },
44520     
44521     slideInIf : function(e){
44522         if(!e.within(this.el)){
44523             this.slideIn();
44524         }
44525     },
44526
44527     animateCollapse : function(){
44528         this.beforeSlide();
44529         this.el.setStyle("z-index", 20000);
44530         var anchor = this.getSlideAnchor();
44531         this.el.slideOut(anchor, {
44532             callback : function(){
44533                 this.el.setStyle("z-index", "");
44534                 this.collapsedEl.slideIn(anchor, {duration:.3});
44535                 this.afterSlide();
44536                 this.el.setLocation(-10000,-10000);
44537                 this.el.hide();
44538                 this.fireEvent("collapsed", this);
44539             },
44540             scope: this,
44541             block: true
44542         });
44543     },
44544
44545     animateExpand : function(){
44546         this.beforeSlide();
44547         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44548         this.el.setStyle("z-index", 20000);
44549         this.collapsedEl.hide({
44550             duration:.1
44551         });
44552         this.el.slideIn(this.getSlideAnchor(), {
44553             callback : function(){
44554                 this.el.setStyle("z-index", "");
44555                 this.afterSlide();
44556                 if(this.split){
44557                     this.split.el.show();
44558                 }
44559                 this.fireEvent("invalidated", this);
44560                 this.fireEvent("expanded", this);
44561             },
44562             scope: this,
44563             block: true
44564         });
44565     },
44566
44567     anchors : {
44568         "west" : "left",
44569         "east" : "right",
44570         "north" : "top",
44571         "south" : "bottom"
44572     },
44573
44574     sanchors : {
44575         "west" : "l",
44576         "east" : "r",
44577         "north" : "t",
44578         "south" : "b"
44579     },
44580
44581     canchors : {
44582         "west" : "tl-tr",
44583         "east" : "tr-tl",
44584         "north" : "tl-bl",
44585         "south" : "bl-tl"
44586     },
44587
44588     getAnchor : function(){
44589         return this.anchors[this.position];
44590     },
44591
44592     getCollapseAnchor : function(){
44593         return this.canchors[this.position];
44594     },
44595
44596     getSlideAnchor : function(){
44597         return this.sanchors[this.position];
44598     },
44599
44600     getAlignAdj : function(){
44601         var cm = this.cmargins;
44602         switch(this.position){
44603             case "west":
44604                 return [0, 0];
44605             break;
44606             case "east":
44607                 return [0, 0];
44608             break;
44609             case "north":
44610                 return [0, 0];
44611             break;
44612             case "south":
44613                 return [0, 0];
44614             break;
44615         }
44616     },
44617
44618     getExpandAdj : function(){
44619         var c = this.collapsedEl, cm = this.cmargins;
44620         switch(this.position){
44621             case "west":
44622                 return [-(cm.right+c.getWidth()+cm.left), 0];
44623             break;
44624             case "east":
44625                 return [cm.right+c.getWidth()+cm.left, 0];
44626             break;
44627             case "north":
44628                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44629             break;
44630             case "south":
44631                 return [0, cm.top+cm.bottom+c.getHeight()];
44632             break;
44633         }
44634     }
44635 });/*
44636  * Based on:
44637  * Ext JS Library 1.1.1
44638  * Copyright(c) 2006-2007, Ext JS, LLC.
44639  *
44640  * Originally Released Under LGPL - original licence link has changed is not relivant.
44641  *
44642  * Fork - LGPL
44643  * <script type="text/javascript">
44644  */
44645 /*
44646  * These classes are private internal classes
44647  */
44648 Roo.bootstrap.layout.Center = function(config){
44649     config.region = "center";
44650     Roo.bootstrap.layout.Region.call(this, config);
44651     this.visible = true;
44652     this.minWidth = config.minWidth || 20;
44653     this.minHeight = config.minHeight || 20;
44654 };
44655
44656 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44657     hide : function(){
44658         // center panel can't be hidden
44659     },
44660     
44661     show : function(){
44662         // center panel can't be hidden
44663     },
44664     
44665     getMinWidth: function(){
44666         return this.minWidth;
44667     },
44668     
44669     getMinHeight: function(){
44670         return this.minHeight;
44671     }
44672 });
44673
44674
44675
44676
44677  
44678
44679
44680
44681
44682
44683
44684 Roo.bootstrap.layout.North = function(config)
44685 {
44686     config.region = 'north';
44687     config.cursor = 'n-resize';
44688     
44689     Roo.bootstrap.layout.Split.call(this, config);
44690     
44691     
44692     if(this.split){
44693         this.split.placement = Roo.bootstrap.SplitBar.TOP;
44694         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44695         this.split.el.addClass("roo-layout-split-v");
44696     }
44697     //var size = config.initialSize || config.height;
44698     //if(this.el && typeof size != "undefined"){
44699     //    this.el.setHeight(size);
44700     //}
44701 };
44702 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44703 {
44704     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44705      
44706      
44707     onRender : function(ctr, pos)
44708     {
44709         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44710         var size = this.config.initialSize || this.config.height;
44711         if(this.el && typeof size != "undefined"){
44712             this.el.setHeight(size);
44713         }
44714     
44715     },
44716     
44717     getBox : function(){
44718         if(this.collapsed){
44719             return this.collapsedEl.getBox();
44720         }
44721         var box = this.el.getBox();
44722         if(this.split){
44723             box.height += this.split.el.getHeight();
44724         }
44725         return box;
44726     },
44727     
44728     updateBox : function(box){
44729         if(this.split && !this.collapsed){
44730             box.height -= this.split.el.getHeight();
44731             this.split.el.setLeft(box.x);
44732             this.split.el.setTop(box.y+box.height);
44733             this.split.el.setWidth(box.width);
44734         }
44735         if(this.collapsed){
44736             this.updateBody(box.width, null);
44737         }
44738         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44739     }
44740 });
44741
44742
44743
44744
44745
44746 Roo.bootstrap.layout.South = function(config){
44747     config.region = 'south';
44748     config.cursor = 's-resize';
44749     Roo.bootstrap.layout.Split.call(this, config);
44750     if(this.split){
44751         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44752         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44753         this.split.el.addClass("roo-layout-split-v");
44754     }
44755     
44756 };
44757
44758 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44759     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44760     
44761     onRender : function(ctr, pos)
44762     {
44763         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44764         var size = this.config.initialSize || this.config.height;
44765         if(this.el && typeof size != "undefined"){
44766             this.el.setHeight(size);
44767         }
44768     
44769     },
44770     
44771     getBox : function(){
44772         if(this.collapsed){
44773             return this.collapsedEl.getBox();
44774         }
44775         var box = this.el.getBox();
44776         if(this.split){
44777             var sh = this.split.el.getHeight();
44778             box.height += sh;
44779             box.y -= sh;
44780         }
44781         return box;
44782     },
44783     
44784     updateBox : function(box){
44785         if(this.split && !this.collapsed){
44786             var sh = this.split.el.getHeight();
44787             box.height -= sh;
44788             box.y += sh;
44789             this.split.el.setLeft(box.x);
44790             this.split.el.setTop(box.y-sh);
44791             this.split.el.setWidth(box.width);
44792         }
44793         if(this.collapsed){
44794             this.updateBody(box.width, null);
44795         }
44796         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44797     }
44798 });
44799
44800 Roo.bootstrap.layout.East = function(config){
44801     config.region = "east";
44802     config.cursor = "e-resize";
44803     Roo.bootstrap.layout.Split.call(this, config);
44804     if(this.split){
44805         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44806         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44807         this.split.el.addClass("roo-layout-split-h");
44808     }
44809     
44810 };
44811 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44812     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44813     
44814     onRender : function(ctr, pos)
44815     {
44816         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44817         var size = this.config.initialSize || this.config.width;
44818         if(this.el && typeof size != "undefined"){
44819             this.el.setWidth(size);
44820         }
44821     
44822     },
44823     
44824     getBox : function(){
44825         if(this.collapsed){
44826             return this.collapsedEl.getBox();
44827         }
44828         var box = this.el.getBox();
44829         if(this.split){
44830             var sw = this.split.el.getWidth();
44831             box.width += sw;
44832             box.x -= sw;
44833         }
44834         return box;
44835     },
44836
44837     updateBox : function(box){
44838         if(this.split && !this.collapsed){
44839             var sw = this.split.el.getWidth();
44840             box.width -= sw;
44841             this.split.el.setLeft(box.x);
44842             this.split.el.setTop(box.y);
44843             this.split.el.setHeight(box.height);
44844             box.x += sw;
44845         }
44846         if(this.collapsed){
44847             this.updateBody(null, box.height);
44848         }
44849         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44850     }
44851 });
44852
44853 Roo.bootstrap.layout.West = function(config){
44854     config.region = "west";
44855     config.cursor = "w-resize";
44856     
44857     Roo.bootstrap.layout.Split.call(this, config);
44858     if(this.split){
44859         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44860         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44861         this.split.el.addClass("roo-layout-split-h");
44862     }
44863     
44864 };
44865 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44866     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44867     
44868     onRender: function(ctr, pos)
44869     {
44870         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44871         var size = this.config.initialSize || this.config.width;
44872         if(typeof size != "undefined"){
44873             this.el.setWidth(size);
44874         }
44875     },
44876     
44877     getBox : function(){
44878         if(this.collapsed){
44879             return this.collapsedEl.getBox();
44880         }
44881         var box = this.el.getBox();
44882         if (box.width == 0) {
44883             box.width = this.config.width; // kludge?
44884         }
44885         if(this.split){
44886             box.width += this.split.el.getWidth();
44887         }
44888         return box;
44889     },
44890     
44891     updateBox : function(box){
44892         if(this.split && !this.collapsed){
44893             var sw = this.split.el.getWidth();
44894             box.width -= sw;
44895             this.split.el.setLeft(box.x+box.width);
44896             this.split.el.setTop(box.y);
44897             this.split.el.setHeight(box.height);
44898         }
44899         if(this.collapsed){
44900             this.updateBody(null, box.height);
44901         }
44902         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44903     }
44904 });/*
44905  * Based on:
44906  * Ext JS Library 1.1.1
44907  * Copyright(c) 2006-2007, Ext JS, LLC.
44908  *
44909  * Originally Released Under LGPL - original licence link has changed is not relivant.
44910  *
44911  * Fork - LGPL
44912  * <script type="text/javascript">
44913  */
44914 /**
44915  * @class Roo.bootstrap.paenl.Content
44916  * @extends Roo.util.Observable
44917  * @children Roo.bootstrap.Component
44918  * @parent builder Roo.bootstrap.layout.Border
44919  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44920  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44921  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44922  * @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
44923  * @cfg {Boolean}   closable      True if the panel can be closed/removed
44924  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
44925  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44926  * @cfg {Toolbar}   toolbar       A toolbar for this panel
44927  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
44928  * @cfg {String} title          The title for this panel
44929  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44930  * @cfg {String} url            Calls {@link #setUrl} with this value
44931  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44932  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
44933  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
44934  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
44935  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
44936  * @cfg {Boolean} badges render the badges
44937  * @cfg {String} cls  extra classes to use  
44938  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44939  
44940  * @constructor
44941  * Create a new ContentPanel.
44942  * @param {String/Object} config A string to set only the title or a config object
44943  
44944  */
44945 Roo.bootstrap.panel.Content = function( config){
44946     
44947     this.tpl = config.tpl || false;
44948     
44949     var el = config.el;
44950     var content = config.content;
44951
44952     if(config.autoCreate){ // xtype is available if this is called from factory
44953         el = Roo.id();
44954     }
44955     this.el = Roo.get(el);
44956     if(!this.el && config && config.autoCreate){
44957         if(typeof config.autoCreate == "object"){
44958             if(!config.autoCreate.id){
44959                 config.autoCreate.id = config.id||el;
44960             }
44961             this.el = Roo.DomHelper.append(document.body,
44962                         config.autoCreate, true);
44963         }else{
44964             var elcfg =  {
44965                 tag: "div",
44966                 cls: (config.cls || '') +
44967                     (config.background ? ' bg-' + config.background : '') +
44968                     " roo-layout-inactive-content",
44969                 id: config.id||el
44970             };
44971             if (config.iframe) {
44972                 elcfg.cn = [
44973                     {
44974                         tag : 'iframe',
44975                         style : 'border: 0px',
44976                         src : 'about:blank'
44977                     }
44978                 ];
44979             }
44980               
44981             if (config.html) {
44982                 elcfg.html = config.html;
44983                 
44984             }
44985                         
44986             this.el = Roo.DomHelper.append(document.body, elcfg , true);
44987             if (config.iframe) {
44988                 this.iframeEl = this.el.select('iframe',true).first();
44989             }
44990             
44991         }
44992     } 
44993     this.closable = false;
44994     this.loaded = false;
44995     this.active = false;
44996    
44997       
44998     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
44999         
45000         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45001         
45002         this.wrapEl = this.el; //this.el.wrap();
45003         var ti = [];
45004         if (config.toolbar.items) {
45005             ti = config.toolbar.items ;
45006             delete config.toolbar.items ;
45007         }
45008         
45009         var nitems = [];
45010         this.toolbar.render(this.wrapEl, 'before');
45011         for(var i =0;i < ti.length;i++) {
45012           //  Roo.log(['add child', items[i]]);
45013             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45014         }
45015         this.toolbar.items = nitems;
45016         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45017         delete config.toolbar;
45018         
45019     }
45020     /*
45021     // xtype created footer. - not sure if will work as we normally have to render first..
45022     if (this.footer && !this.footer.el && this.footer.xtype) {
45023         if (!this.wrapEl) {
45024             this.wrapEl = this.el.wrap();
45025         }
45026     
45027         this.footer.container = this.wrapEl.createChild();
45028          
45029         this.footer = Roo.factory(this.footer, Roo);
45030         
45031     }
45032     */
45033     
45034      if(typeof config == "string"){
45035         this.title = config;
45036     }else{
45037         Roo.apply(this, config);
45038     }
45039     
45040     if(this.resizeEl){
45041         this.resizeEl = Roo.get(this.resizeEl, true);
45042     }else{
45043         this.resizeEl = this.el;
45044     }
45045     // handle view.xtype
45046     
45047  
45048     
45049     
45050     this.addEvents({
45051         /**
45052          * @event activate
45053          * Fires when this panel is activated. 
45054          * @param {Roo.ContentPanel} this
45055          */
45056         "activate" : true,
45057         /**
45058          * @event deactivate
45059          * Fires when this panel is activated. 
45060          * @param {Roo.ContentPanel} this
45061          */
45062         "deactivate" : true,
45063
45064         /**
45065          * @event resize
45066          * Fires when this panel is resized if fitToFrame is true.
45067          * @param {Roo.ContentPanel} this
45068          * @param {Number} width The width after any component adjustments
45069          * @param {Number} height The height after any component adjustments
45070          */
45071         "resize" : true,
45072         
45073          /**
45074          * @event render
45075          * Fires when this tab is created
45076          * @param {Roo.ContentPanel} this
45077          */
45078         "render" : true,
45079         
45080           /**
45081          * @event scroll
45082          * Fires when this content is scrolled
45083          * @param {Roo.ContentPanel} this
45084          * @param {Event} scrollEvent
45085          */
45086         "scroll" : true
45087         
45088         
45089         
45090     });
45091     
45092
45093     
45094     
45095     if(this.autoScroll && !this.iframe){
45096         this.resizeEl.setStyle("overflow", "auto");
45097         this.resizeEl.on('scroll', this.onScroll, this);
45098     } else {
45099         // fix randome scrolling
45100         //this.el.on('scroll', function() {
45101         //    Roo.log('fix random scolling');
45102         //    this.scrollTo('top',0); 
45103         //});
45104     }
45105     content = content || this.content;
45106     if(content){
45107         this.setContent(content);
45108     }
45109     if(config && config.url){
45110         this.setUrl(this.url, this.params, this.loadOnce);
45111     }
45112     
45113     
45114     
45115     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45116     
45117     if (this.view && typeof(this.view.xtype) != 'undefined') {
45118         this.view.el = this.el.appendChild(document.createElement("div"));
45119         this.view = Roo.factory(this.view); 
45120         this.view.render  &&  this.view.render(false, '');  
45121     }
45122     
45123     
45124     this.fireEvent('render', this);
45125 };
45126
45127 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45128     
45129     cls : '',
45130     background : '',
45131     
45132     tabTip : '',
45133     
45134     iframe : false,
45135     iframeEl : false,
45136     
45137     /* Resize Element - use this to work out scroll etc. */
45138     resizeEl : false,
45139     
45140     setRegion : function(region){
45141         this.region = region;
45142         this.setActiveClass(region && !this.background);
45143     },
45144     
45145     
45146     setActiveClass: function(state)
45147     {
45148         if(state){
45149            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45150            this.el.setStyle('position','relative');
45151         }else{
45152            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45153            this.el.setStyle('position', 'absolute');
45154         } 
45155     },
45156     
45157     /**
45158      * Returns the toolbar for this Panel if one was configured. 
45159      * @return {Roo.Toolbar} 
45160      */
45161     getToolbar : function(){
45162         return this.toolbar;
45163     },
45164     
45165     setActiveState : function(active)
45166     {
45167         this.active = active;
45168         this.setActiveClass(active);
45169         if(!active){
45170             if(this.fireEvent("deactivate", this) === false){
45171                 return false;
45172             }
45173             return true;
45174         }
45175         this.fireEvent("activate", this);
45176         return true;
45177     },
45178     /**
45179      * Updates this panel's element (not for iframe)
45180      * @param {String} content The new content
45181      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45182     */
45183     setContent : function(content, loadScripts){
45184         if (this.iframe) {
45185             return;
45186         }
45187         
45188         this.el.update(content, loadScripts);
45189     },
45190
45191     ignoreResize : function(w, h)
45192     {
45193         //return false; // always resize?
45194         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45195             return true;
45196         }else{
45197             this.lastSize = {width: w, height: h};
45198             return false;
45199         }
45200     },
45201     /**
45202      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45203      * @return {Roo.UpdateManager} The UpdateManager
45204      */
45205     getUpdateManager : function(){
45206         if (this.iframe) {
45207             return false;
45208         }
45209         return this.el.getUpdateManager();
45210     },
45211      /**
45212      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45213      * Does not work with IFRAME contents
45214      * @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:
45215 <pre><code>
45216 panel.load({
45217     url: "your-url.php",
45218     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45219     callback: yourFunction,
45220     scope: yourObject, //(optional scope)
45221     discardUrl: false,
45222     nocache: false,
45223     text: "Loading...",
45224     timeout: 30,
45225     scripts: false
45226 });
45227 </code></pre>
45228      
45229      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45230      * 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.
45231      * @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}
45232      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45233      * @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.
45234      * @return {Roo.ContentPanel} this
45235      */
45236     load : function(){
45237         
45238         if (this.iframe) {
45239             return this;
45240         }
45241         
45242         var um = this.el.getUpdateManager();
45243         um.update.apply(um, arguments);
45244         return this;
45245     },
45246
45247
45248     /**
45249      * 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.
45250      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45251      * @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)
45252      * @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)
45253      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45254      */
45255     setUrl : function(url, params, loadOnce){
45256         if (this.iframe) {
45257             this.iframeEl.dom.src = url;
45258             return false;
45259         }
45260         
45261         if(this.refreshDelegate){
45262             this.removeListener("activate", this.refreshDelegate);
45263         }
45264         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45265         this.on("activate", this.refreshDelegate);
45266         return this.el.getUpdateManager();
45267     },
45268     
45269     _handleRefresh : function(url, params, loadOnce){
45270         if(!loadOnce || !this.loaded){
45271             var updater = this.el.getUpdateManager();
45272             updater.update(url, params, this._setLoaded.createDelegate(this));
45273         }
45274     },
45275     
45276     _setLoaded : function(){
45277         this.loaded = true;
45278     }, 
45279     
45280     /**
45281      * Returns this panel's id
45282      * @return {String} 
45283      */
45284     getId : function(){
45285         return this.el.id;
45286     },
45287     
45288     /** 
45289      * Returns this panel's element - used by regiosn to add.
45290      * @return {Roo.Element} 
45291      */
45292     getEl : function(){
45293         return this.wrapEl || this.el;
45294     },
45295     
45296    
45297     
45298     adjustForComponents : function(width, height)
45299     {
45300         //Roo.log('adjustForComponents ');
45301         if(this.resizeEl != this.el){
45302             width -= this.el.getFrameWidth('lr');
45303             height -= this.el.getFrameWidth('tb');
45304         }
45305         if(this.toolbar){
45306             var te = this.toolbar.getEl();
45307             te.setWidth(width);
45308             height -= te.getHeight();
45309         }
45310         if(this.footer){
45311             var te = this.footer.getEl();
45312             te.setWidth(width);
45313             height -= te.getHeight();
45314         }
45315         
45316         
45317         if(this.adjustments){
45318             width += this.adjustments[0];
45319             height += this.adjustments[1];
45320         }
45321         return {"width": width, "height": height};
45322     },
45323     
45324     setSize : function(width, height){
45325         if(this.fitToFrame && !this.ignoreResize(width, height)){
45326             if(this.fitContainer && this.resizeEl != this.el){
45327                 this.el.setSize(width, height);
45328             }
45329             var size = this.adjustForComponents(width, height);
45330             if (this.iframe) {
45331                 this.iframeEl.setSize(width,height);
45332             }
45333             
45334             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45335             this.fireEvent('resize', this, size.width, size.height);
45336             
45337             
45338         }
45339     },
45340     
45341     /**
45342      * Returns this panel's title
45343      * @return {String} 
45344      */
45345     getTitle : function(){
45346         
45347         if (typeof(this.title) != 'object') {
45348             return this.title;
45349         }
45350         
45351         var t = '';
45352         for (var k in this.title) {
45353             if (!this.title.hasOwnProperty(k)) {
45354                 continue;
45355             }
45356             
45357             if (k.indexOf('-') >= 0) {
45358                 var s = k.split('-');
45359                 for (var i = 0; i<s.length; i++) {
45360                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45361                 }
45362             } else {
45363                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45364             }
45365         }
45366         return t;
45367     },
45368     
45369     /**
45370      * Set this panel's title
45371      * @param {String} title
45372      */
45373     setTitle : function(title){
45374         this.title = title;
45375         if(this.region){
45376             this.region.updatePanelTitle(this, title);
45377         }
45378     },
45379     
45380     /**
45381      * Returns true is this panel was configured to be closable
45382      * @return {Boolean} 
45383      */
45384     isClosable : function(){
45385         return this.closable;
45386     },
45387     
45388     beforeSlide : function(){
45389         this.el.clip();
45390         this.resizeEl.clip();
45391     },
45392     
45393     afterSlide : function(){
45394         this.el.unclip();
45395         this.resizeEl.unclip();
45396     },
45397     
45398     /**
45399      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45400      *   Will fail silently if the {@link #setUrl} method has not been called.
45401      *   This does not activate the panel, just updates its content.
45402      */
45403     refresh : function(){
45404         if(this.refreshDelegate){
45405            this.loaded = false;
45406            this.refreshDelegate();
45407         }
45408     },
45409     
45410     /**
45411      * Destroys this panel
45412      */
45413     destroy : function(){
45414         this.el.removeAllListeners();
45415         var tempEl = document.createElement("span");
45416         tempEl.appendChild(this.el.dom);
45417         tempEl.innerHTML = "";
45418         this.el.remove();
45419         this.el = null;
45420     },
45421     
45422     /**
45423      * form - if the content panel contains a form - this is a reference to it.
45424      * @type {Roo.form.Form}
45425      */
45426     form : false,
45427     /**
45428      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45429      *    This contains a reference to it.
45430      * @type {Roo.View}
45431      */
45432     view : false,
45433     
45434       /**
45435      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45436      * <pre><code>
45437
45438 layout.addxtype({
45439        xtype : 'Form',
45440        items: [ .... ]
45441    }
45442 );
45443
45444 </code></pre>
45445      * @param {Object} cfg Xtype definition of item to add.
45446      */
45447     
45448     
45449     getChildContainer: function () {
45450         return this.getEl();
45451     },
45452     
45453     
45454     onScroll : function(e)
45455     {
45456         this.fireEvent('scroll', this, e);
45457     }
45458     
45459     
45460     /*
45461         var  ret = new Roo.factory(cfg);
45462         return ret;
45463         
45464         
45465         // add form..
45466         if (cfg.xtype.match(/^Form$/)) {
45467             
45468             var el;
45469             //if (this.footer) {
45470             //    el = this.footer.container.insertSibling(false, 'before');
45471             //} else {
45472                 el = this.el.createChild();
45473             //}
45474
45475             this.form = new  Roo.form.Form(cfg);
45476             
45477             
45478             if ( this.form.allItems.length) {
45479                 this.form.render(el.dom);
45480             }
45481             return this.form;
45482         }
45483         // should only have one of theses..
45484         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45485             // views.. should not be just added - used named prop 'view''
45486             
45487             cfg.el = this.el.appendChild(document.createElement("div"));
45488             // factory?
45489             
45490             var ret = new Roo.factory(cfg);
45491              
45492              ret.render && ret.render(false, ''); // render blank..
45493             this.view = ret;
45494             return ret;
45495         }
45496         return false;
45497     }
45498     \*/
45499 });
45500  
45501 /**
45502  * @class Roo.bootstrap.panel.Grid
45503  * @extends Roo.bootstrap.panel.Content
45504  * @constructor
45505  * Create a new GridPanel.
45506  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45507  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45508  * @param {Object} config A the config object
45509   
45510  */
45511
45512
45513
45514 Roo.bootstrap.panel.Grid = function(config)
45515 {
45516     
45517       
45518     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45519         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45520
45521     config.el = this.wrapper;
45522     //this.el = this.wrapper;
45523     
45524       if (config.container) {
45525         // ctor'ed from a Border/panel.grid
45526         
45527         
45528         this.wrapper.setStyle("overflow", "hidden");
45529         this.wrapper.addClass('roo-grid-container');
45530
45531     }
45532     
45533     
45534     if(config.toolbar){
45535         var tool_el = this.wrapper.createChild();    
45536         this.toolbar = Roo.factory(config.toolbar);
45537         var ti = [];
45538         if (config.toolbar.items) {
45539             ti = config.toolbar.items ;
45540             delete config.toolbar.items ;
45541         }
45542         
45543         var nitems = [];
45544         this.toolbar.render(tool_el);
45545         for(var i =0;i < ti.length;i++) {
45546           //  Roo.log(['add child', items[i]]);
45547             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45548         }
45549         this.toolbar.items = nitems;
45550         
45551         delete config.toolbar;
45552     }
45553     
45554     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45555     config.grid.scrollBody = true;;
45556     config.grid.monitorWindowResize = false; // turn off autosizing
45557     config.grid.autoHeight = false;
45558     config.grid.autoWidth = false;
45559     
45560     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45561     
45562     if (config.background) {
45563         // render grid on panel activation (if panel background)
45564         this.on('activate', function(gp) {
45565             if (!gp.grid.rendered) {
45566                 gp.grid.render(this.wrapper);
45567                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45568             }
45569         });
45570             
45571     } else {
45572         this.grid.render(this.wrapper);
45573         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45574
45575     }
45576     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45577     // ??? needed ??? config.el = this.wrapper;
45578     
45579     
45580     
45581   
45582     // xtype created footer. - not sure if will work as we normally have to render first..
45583     if (this.footer && !this.footer.el && this.footer.xtype) {
45584         
45585         var ctr = this.grid.getView().getFooterPanel(true);
45586         this.footer.dataSource = this.grid.dataSource;
45587         this.footer = Roo.factory(this.footer, Roo);
45588         this.footer.render(ctr);
45589         
45590     }
45591     
45592     
45593     
45594     
45595      
45596 };
45597
45598 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45599 {
45600   
45601     getId : function(){
45602         return this.grid.id;
45603     },
45604     
45605     /**
45606      * Returns the grid for this panel
45607      * @return {Roo.bootstrap.Table} 
45608      */
45609     getGrid : function(){
45610         return this.grid;    
45611     },
45612     
45613     setSize : function(width, height)
45614     {
45615      
45616         //if(!this.ignoreResize(width, height)){
45617             var grid = this.grid;
45618             var size = this.adjustForComponents(width, height);
45619             // tfoot is not a footer?
45620           
45621             
45622             var gridel = grid.getGridEl();
45623             gridel.setSize(size.width, size.height);
45624             
45625             var tbd = grid.getGridEl().select('tbody', true).first();
45626             var thd = grid.getGridEl().select('thead',true).first();
45627             var tbf= grid.getGridEl().select('tfoot', true).first();
45628
45629             if (tbf) {
45630                 size.height -= tbf.getHeight();
45631             }
45632             if (thd) {
45633                 size.height -= thd.getHeight();
45634             }
45635             
45636             tbd.setSize(size.width, size.height );
45637             // this is for the account management tab -seems to work there.
45638             var thd = grid.getGridEl().select('thead',true).first();
45639             //if (tbd) {
45640             //    tbd.setSize(size.width, size.height - thd.getHeight());
45641             //}
45642              
45643             grid.autoSize();
45644         //}
45645    
45646     },
45647      
45648     
45649     
45650     beforeSlide : function(){
45651         this.grid.getView().scroller.clip();
45652     },
45653     
45654     afterSlide : function(){
45655         this.grid.getView().scroller.unclip();
45656     },
45657     
45658     destroy : function(){
45659         this.grid.destroy();
45660         delete this.grid;
45661         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
45662     }
45663 });
45664
45665 /**
45666  * @class Roo.bootstrap.panel.Nest
45667  * @extends Roo.bootstrap.panel.Content
45668  * @constructor
45669  * Create a new Panel, that can contain a layout.Border.
45670  * 
45671  * 
45672  * @param {String/Object} config A string to set only the title or a config object
45673  */
45674 Roo.bootstrap.panel.Nest = function(config)
45675 {
45676     // construct with only one argument..
45677     /* FIXME - implement nicer consturctors
45678     if (layout.layout) {
45679         config = layout;
45680         layout = config.layout;
45681         delete config.layout;
45682     }
45683     if (layout.xtype && !layout.getEl) {
45684         // then layout needs constructing..
45685         layout = Roo.factory(layout, Roo);
45686     }
45687     */
45688     
45689     config.el =  config.layout.getEl();
45690     
45691     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45692     
45693     config.layout.monitorWindowResize = false; // turn off autosizing
45694     this.layout = config.layout;
45695     this.layout.getEl().addClass("roo-layout-nested-layout");
45696     this.layout.parent = this;
45697     
45698     
45699     
45700     
45701 };
45702
45703 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45704     /**
45705     * @cfg {Roo.BorderLayout} layout The layout for this panel
45706     */
45707     layout : false,
45708
45709     setSize : function(width, height){
45710         if(!this.ignoreResize(width, height)){
45711             var size = this.adjustForComponents(width, height);
45712             var el = this.layout.getEl();
45713             if (size.height < 1) {
45714                 el.setWidth(size.width);   
45715             } else {
45716                 el.setSize(size.width, size.height);
45717             }
45718             var touch = el.dom.offsetWidth;
45719             this.layout.layout();
45720             // ie requires a double layout on the first pass
45721             if(Roo.isIE && !this.initialized){
45722                 this.initialized = true;
45723                 this.layout.layout();
45724             }
45725         }
45726     },
45727     
45728     // activate all subpanels if not currently active..
45729     
45730     setActiveState : function(active){
45731         this.active = active;
45732         this.setActiveClass(active);
45733         
45734         if(!active){
45735             this.fireEvent("deactivate", this);
45736             return;
45737         }
45738         
45739         this.fireEvent("activate", this);
45740         // not sure if this should happen before or after..
45741         if (!this.layout) {
45742             return; // should not happen..
45743         }
45744         var reg = false;
45745         for (var r in this.layout.regions) {
45746             reg = this.layout.getRegion(r);
45747             if (reg.getActivePanel()) {
45748                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45749                 reg.setActivePanel(reg.getActivePanel());
45750                 continue;
45751             }
45752             if (!reg.panels.length) {
45753                 continue;
45754             }
45755             reg.showPanel(reg.getPanel(0));
45756         }
45757         
45758         
45759         
45760         
45761     },
45762     
45763     /**
45764      * Returns the nested BorderLayout for this panel
45765      * @return {Roo.BorderLayout} 
45766      */
45767     getLayout : function(){
45768         return this.layout;
45769     },
45770     
45771      /**
45772      * Adds a xtype elements to the layout of the nested panel
45773      * <pre><code>
45774
45775 panel.addxtype({
45776        xtype : 'ContentPanel',
45777        region: 'west',
45778        items: [ .... ]
45779    }
45780 );
45781
45782 panel.addxtype({
45783         xtype : 'NestedLayoutPanel',
45784         region: 'west',
45785         layout: {
45786            center: { },
45787            west: { }   
45788         },
45789         items : [ ... list of content panels or nested layout panels.. ]
45790    }
45791 );
45792 </code></pre>
45793      * @param {Object} cfg Xtype definition of item to add.
45794      */
45795     addxtype : function(cfg) {
45796         return this.layout.addxtype(cfg);
45797     
45798     }
45799 });/*
45800  * Based on:
45801  * Ext JS Library 1.1.1
45802  * Copyright(c) 2006-2007, Ext JS, LLC.
45803  *
45804  * Originally Released Under LGPL - original licence link has changed is not relivant.
45805  *
45806  * Fork - LGPL
45807  * <script type="text/javascript">
45808  */
45809 /**
45810  * @class Roo.TabPanel
45811  * @extends Roo.util.Observable
45812  * A lightweight tab container.
45813  * <br><br>
45814  * Usage:
45815  * <pre><code>
45816 // basic tabs 1, built from existing content
45817 var tabs = new Roo.TabPanel("tabs1");
45818 tabs.addTab("script", "View Script");
45819 tabs.addTab("markup", "View Markup");
45820 tabs.activate("script");
45821
45822 // more advanced tabs, built from javascript
45823 var jtabs = new Roo.TabPanel("jtabs");
45824 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45825
45826 // set up the UpdateManager
45827 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45828 var updater = tab2.getUpdateManager();
45829 updater.setDefaultUrl("ajax1.htm");
45830 tab2.on('activate', updater.refresh, updater, true);
45831
45832 // Use setUrl for Ajax loading
45833 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45834 tab3.setUrl("ajax2.htm", null, true);
45835
45836 // Disabled tab
45837 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45838 tab4.disable();
45839
45840 jtabs.activate("jtabs-1");
45841  * </code></pre>
45842  * @constructor
45843  * Create a new TabPanel.
45844  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45845  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45846  */
45847 Roo.bootstrap.panel.Tabs = function(config){
45848     /**
45849     * The container element for this TabPanel.
45850     * @type Roo.Element
45851     */
45852     this.el = Roo.get(config.el);
45853     delete config.el;
45854     if(config){
45855         if(typeof config == "boolean"){
45856             this.tabPosition = config ? "bottom" : "top";
45857         }else{
45858             Roo.apply(this, config);
45859         }
45860     }
45861     
45862     if(this.tabPosition == "bottom"){
45863         // if tabs are at the bottom = create the body first.
45864         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45865         this.el.addClass("roo-tabs-bottom");
45866     }
45867     // next create the tabs holders
45868     
45869     if (this.tabPosition == "west"){
45870         
45871         var reg = this.region; // fake it..
45872         while (reg) {
45873             if (!reg.mgr.parent) {
45874                 break;
45875             }
45876             reg = reg.mgr.parent.region;
45877         }
45878         Roo.log("got nest?");
45879         Roo.log(reg);
45880         if (reg.mgr.getRegion('west')) {
45881             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45882             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45883             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45884             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45885             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45886         
45887             
45888         }
45889         
45890         
45891     } else {
45892      
45893         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45894         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45895         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45896         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45897     }
45898     
45899     
45900     if(Roo.isIE){
45901         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45902     }
45903     
45904     // finally - if tabs are at the top, then create the body last..
45905     if(this.tabPosition != "bottom"){
45906         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45907          * @type Roo.Element
45908          */
45909         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45910         this.el.addClass("roo-tabs-top");
45911     }
45912     this.items = [];
45913
45914     this.bodyEl.setStyle("position", "relative");
45915
45916     this.active = null;
45917     this.activateDelegate = this.activate.createDelegate(this);
45918
45919     this.addEvents({
45920         /**
45921          * @event tabchange
45922          * Fires when the active tab changes
45923          * @param {Roo.TabPanel} this
45924          * @param {Roo.TabPanelItem} activePanel The new active tab
45925          */
45926         "tabchange": true,
45927         /**
45928          * @event beforetabchange
45929          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45930          * @param {Roo.TabPanel} this
45931          * @param {Object} e Set cancel to true on this object to cancel the tab change
45932          * @param {Roo.TabPanelItem} tab The tab being changed to
45933          */
45934         "beforetabchange" : true
45935     });
45936
45937     Roo.EventManager.onWindowResize(this.onResize, this);
45938     this.cpad = this.el.getPadding("lr");
45939     this.hiddenCount = 0;
45940
45941
45942     // toolbar on the tabbar support...
45943     if (this.toolbar) {
45944         alert("no toolbar support yet");
45945         this.toolbar  = false;
45946         /*
45947         var tcfg = this.toolbar;
45948         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
45949         this.toolbar = new Roo.Toolbar(tcfg);
45950         if (Roo.isSafari) {
45951             var tbl = tcfg.container.child('table', true);
45952             tbl.setAttribute('width', '100%');
45953         }
45954         */
45955         
45956     }
45957    
45958
45959
45960     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45961 };
45962
45963 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45964     /*
45965      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45966      */
45967     tabPosition : "top",
45968     /*
45969      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45970      */
45971     currentTabWidth : 0,
45972     /*
45973      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45974      */
45975     minTabWidth : 40,
45976     /*
45977      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
45978      */
45979     maxTabWidth : 250,
45980     /*
45981      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
45982      */
45983     preferredTabWidth : 175,
45984     /*
45985      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
45986      */
45987     resizeTabs : false,
45988     /*
45989      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
45990      */
45991     monitorResize : true,
45992     /*
45993      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
45994      */
45995     toolbar : false,  // set by caller..
45996     
45997     region : false, /// set by caller
45998     
45999     disableTooltips : true, // not used yet...
46000
46001     /**
46002      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46003      * @param {String} id The id of the div to use <b>or create</b>
46004      * @param {String} text The text for the tab
46005      * @param {String} content (optional) Content to put in the TabPanelItem body
46006      * @param {Boolean} closable (optional) True to create a close icon on the tab
46007      * @return {Roo.TabPanelItem} The created TabPanelItem
46008      */
46009     addTab : function(id, text, content, closable, tpl)
46010     {
46011         var item = new Roo.bootstrap.panel.TabItem({
46012             panel: this,
46013             id : id,
46014             text : text,
46015             closable : closable,
46016             tpl : tpl
46017         });
46018         this.addTabItem(item);
46019         if(content){
46020             item.setContent(content);
46021         }
46022         return item;
46023     },
46024
46025     /**
46026      * Returns the {@link Roo.TabPanelItem} with the specified id/index
46027      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46028      * @return {Roo.TabPanelItem}
46029      */
46030     getTab : function(id){
46031         return this.items[id];
46032     },
46033
46034     /**
46035      * Hides the {@link Roo.TabPanelItem} with the specified id/index
46036      * @param {String/Number} id The id or index of the TabPanelItem to hide.
46037      */
46038     hideTab : function(id){
46039         var t = this.items[id];
46040         if(!t.isHidden()){
46041            t.setHidden(true);
46042            this.hiddenCount++;
46043            this.autoSizeTabs();
46044         }
46045     },
46046
46047     /**
46048      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46049      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46050      */
46051     unhideTab : function(id){
46052         var t = this.items[id];
46053         if(t.isHidden()){
46054            t.setHidden(false);
46055            this.hiddenCount--;
46056            this.autoSizeTabs();
46057         }
46058     },
46059
46060     /**
46061      * Adds an existing {@link Roo.TabPanelItem}.
46062      * @param {Roo.TabPanelItem} item The TabPanelItem to add
46063      */
46064     addTabItem : function(item)
46065     {
46066         this.items[item.id] = item;
46067         this.items.push(item);
46068         this.autoSizeTabs();
46069       //  if(this.resizeTabs){
46070     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46071   //         this.autoSizeTabs();
46072 //        }else{
46073 //            item.autoSize();
46074        // }
46075     },
46076
46077     /**
46078      * Removes a {@link Roo.TabPanelItem}.
46079      * @param {String/Number} id The id or index of the TabPanelItem to remove.
46080      */
46081     removeTab : function(id){
46082         var items = this.items;
46083         var tab = items[id];
46084         if(!tab) { return; }
46085         var index = items.indexOf(tab);
46086         if(this.active == tab && items.length > 1){
46087             var newTab = this.getNextAvailable(index);
46088             if(newTab) {
46089                 newTab.activate();
46090             }
46091         }
46092         this.stripEl.dom.removeChild(tab.pnode.dom);
46093         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46094             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46095         }
46096         items.splice(index, 1);
46097         delete this.items[tab.id];
46098         tab.fireEvent("close", tab);
46099         tab.purgeListeners();
46100         this.autoSizeTabs();
46101     },
46102
46103     getNextAvailable : function(start){
46104         var items = this.items;
46105         var index = start;
46106         // look for a next tab that will slide over to
46107         // replace the one being removed
46108         while(index < items.length){
46109             var item = items[++index];
46110             if(item && !item.isHidden()){
46111                 return item;
46112             }
46113         }
46114         // if one isn't found select the previous tab (on the left)
46115         index = start;
46116         while(index >= 0){
46117             var item = items[--index];
46118             if(item && !item.isHidden()){
46119                 return item;
46120             }
46121         }
46122         return null;
46123     },
46124
46125     /**
46126      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46127      * @param {String/Number} id The id or index of the TabPanelItem to disable.
46128      */
46129     disableTab : function(id){
46130         var tab = this.items[id];
46131         if(tab && this.active != tab){
46132             tab.disable();
46133         }
46134     },
46135
46136     /**
46137      * Enables a {@link Roo.TabPanelItem} that is disabled.
46138      * @param {String/Number} id The id or index of the TabPanelItem to enable.
46139      */
46140     enableTab : function(id){
46141         var tab = this.items[id];
46142         tab.enable();
46143     },
46144
46145     /**
46146      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46147      * @param {String/Number} id The id or index of the TabPanelItem to activate.
46148      * @return {Roo.TabPanelItem} The TabPanelItem.
46149      */
46150     activate : function(id)
46151     {
46152         //Roo.log('activite:'  + id);
46153         
46154         var tab = this.items[id];
46155         if(!tab){
46156             return null;
46157         }
46158         if(tab == this.active || tab.disabled){
46159             return tab;
46160         }
46161         var e = {};
46162         this.fireEvent("beforetabchange", this, e, tab);
46163         if(e.cancel !== true && !tab.disabled){
46164             if(this.active){
46165                 this.active.hide();
46166             }
46167             this.active = this.items[id];
46168             this.active.show();
46169             this.fireEvent("tabchange", this, this.active);
46170         }
46171         return tab;
46172     },
46173
46174     /**
46175      * Gets the active {@link Roo.TabPanelItem}.
46176      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46177      */
46178     getActiveTab : function(){
46179         return this.active;
46180     },
46181
46182     /**
46183      * Updates the tab body element to fit the height of the container element
46184      * for overflow scrolling
46185      * @param {Number} targetHeight (optional) Override the starting height from the elements height
46186      */
46187     syncHeight : function(targetHeight){
46188         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46189         var bm = this.bodyEl.getMargins();
46190         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46191         this.bodyEl.setHeight(newHeight);
46192         return newHeight;
46193     },
46194
46195     onResize : function(){
46196         if(this.monitorResize){
46197             this.autoSizeTabs();
46198         }
46199     },
46200
46201     /**
46202      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46203      */
46204     beginUpdate : function(){
46205         this.updating = true;
46206     },
46207
46208     /**
46209      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46210      */
46211     endUpdate : function(){
46212         this.updating = false;
46213         this.autoSizeTabs();
46214     },
46215
46216     /**
46217      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46218      */
46219     autoSizeTabs : function()
46220     {
46221         var count = this.items.length;
46222         var vcount = count - this.hiddenCount;
46223         
46224         if (vcount < 2) {
46225             this.stripEl.hide();
46226         } else {
46227             this.stripEl.show();
46228         }
46229         
46230         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46231             return;
46232         }
46233         
46234         
46235         var w = Math.max(this.el.getWidth() - this.cpad, 10);
46236         var availWidth = Math.floor(w / vcount);
46237         var b = this.stripBody;
46238         if(b.getWidth() > w){
46239             var tabs = this.items;
46240             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46241             if(availWidth < this.minTabWidth){
46242                 /*if(!this.sleft){    // incomplete scrolling code
46243                     this.createScrollButtons();
46244                 }
46245                 this.showScroll();
46246                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46247             }
46248         }else{
46249             if(this.currentTabWidth < this.preferredTabWidth){
46250                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46251             }
46252         }
46253     },
46254
46255     /**
46256      * Returns the number of tabs in this TabPanel.
46257      * @return {Number}
46258      */
46259      getCount : function(){
46260          return this.items.length;
46261      },
46262
46263     /**
46264      * Resizes all the tabs to the passed width
46265      * @param {Number} The new width
46266      */
46267     setTabWidth : function(width){
46268         this.currentTabWidth = width;
46269         for(var i = 0, len = this.items.length; i < len; i++) {
46270                 if(!this.items[i].isHidden()) {
46271                 this.items[i].setWidth(width);
46272             }
46273         }
46274     },
46275
46276     /**
46277      * Destroys this TabPanel
46278      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46279      */
46280     destroy : function(removeEl){
46281         Roo.EventManager.removeResizeListener(this.onResize, this);
46282         for(var i = 0, len = this.items.length; i < len; i++){
46283             this.items[i].purgeListeners();
46284         }
46285         if(removeEl === true){
46286             this.el.update("");
46287             this.el.remove();
46288         }
46289     },
46290     
46291     createStrip : function(container)
46292     {
46293         var strip = document.createElement("nav");
46294         strip.className = Roo.bootstrap.version == 4 ?
46295             "navbar-light bg-light" : 
46296             "navbar navbar-default"; //"x-tabs-wrap";
46297         container.appendChild(strip);
46298         return strip;
46299     },
46300     
46301     createStripList : function(strip)
46302     {
46303         // div wrapper for retard IE
46304         // returns the "tr" element.
46305         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46306         //'<div class="x-tabs-strip-wrap">'+
46307           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46308           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46309         return strip.firstChild; //.firstChild.firstChild.firstChild;
46310     },
46311     createBody : function(container)
46312     {
46313         var body = document.createElement("div");
46314         Roo.id(body, "tab-body");
46315         //Roo.fly(body).addClass("x-tabs-body");
46316         Roo.fly(body).addClass("tab-content");
46317         container.appendChild(body);
46318         return body;
46319     },
46320     createItemBody :function(bodyEl, id){
46321         var body = Roo.getDom(id);
46322         if(!body){
46323             body = document.createElement("div");
46324             body.id = id;
46325         }
46326         //Roo.fly(body).addClass("x-tabs-item-body");
46327         Roo.fly(body).addClass("tab-pane");
46328          bodyEl.insertBefore(body, bodyEl.firstChild);
46329         return body;
46330     },
46331     /** @private */
46332     createStripElements :  function(stripEl, text, closable, tpl)
46333     {
46334         var td = document.createElement("li"); // was td..
46335         td.className = 'nav-item';
46336         
46337         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46338         
46339         
46340         stripEl.appendChild(td);
46341         /*if(closable){
46342             td.className = "x-tabs-closable";
46343             if(!this.closeTpl){
46344                 this.closeTpl = new Roo.Template(
46345                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46346                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46347                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
46348                 );
46349             }
46350             var el = this.closeTpl.overwrite(td, {"text": text});
46351             var close = el.getElementsByTagName("div")[0];
46352             var inner = el.getElementsByTagName("em")[0];
46353             return {"el": el, "close": close, "inner": inner};
46354         } else {
46355         */
46356         // not sure what this is..
46357 //            if(!this.tabTpl){
46358                 //this.tabTpl = new Roo.Template(
46359                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46360                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46361                 //);
46362 //                this.tabTpl = new Roo.Template(
46363 //                   '<a href="#">' +
46364 //                   '<span unselectable="on"' +
46365 //                            (this.disableTooltips ? '' : ' title="{text}"') +
46366 //                            ' >{text}</span></a>'
46367 //                );
46368 //                
46369 //            }
46370
46371
46372             var template = tpl || this.tabTpl || false;
46373             
46374             if(!template){
46375                 template =  new Roo.Template(
46376                         Roo.bootstrap.version == 4 ? 
46377                             (
46378                                 '<a class="nav-link" href="#" unselectable="on"' +
46379                                      (this.disableTooltips ? '' : ' title="{text}"') +
46380                                      ' >{text}</a>'
46381                             ) : (
46382                                 '<a class="nav-link" href="#">' +
46383                                 '<span unselectable="on"' +
46384                                          (this.disableTooltips ? '' : ' title="{text}"') +
46385                                     ' >{text}</span></a>'
46386                             )
46387                 );
46388             }
46389             
46390             switch (typeof(template)) {
46391                 case 'object' :
46392                     break;
46393                 case 'string' :
46394                     template = new Roo.Template(template);
46395                     break;
46396                 default :
46397                     break;
46398             }
46399             
46400             var el = template.overwrite(td, {"text": text});
46401             
46402             var inner = el.getElementsByTagName("span")[0];
46403             
46404             return {"el": el, "inner": inner};
46405             
46406     }
46407         
46408     
46409 });
46410
46411 /**
46412  * @class Roo.TabPanelItem
46413  * @extends Roo.util.Observable
46414  * Represents an individual item (tab plus body) in a TabPanel.
46415  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46416  * @param {String} id The id of this TabPanelItem
46417  * @param {String} text The text for the tab of this TabPanelItem
46418  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46419  */
46420 Roo.bootstrap.panel.TabItem = function(config){
46421     /**
46422      * The {@link Roo.TabPanel} this TabPanelItem belongs to
46423      * @type Roo.TabPanel
46424      */
46425     this.tabPanel = config.panel;
46426     /**
46427      * The id for this TabPanelItem
46428      * @type String
46429      */
46430     this.id = config.id;
46431     /** @private */
46432     this.disabled = false;
46433     /** @private */
46434     this.text = config.text;
46435     /** @private */
46436     this.loaded = false;
46437     this.closable = config.closable;
46438
46439     /**
46440      * The body element for this TabPanelItem.
46441      * @type Roo.Element
46442      */
46443     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46444     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46445     this.bodyEl.setStyle("display", "block");
46446     this.bodyEl.setStyle("zoom", "1");
46447     //this.hideAction();
46448
46449     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46450     /** @private */
46451     this.el = Roo.get(els.el);
46452     this.inner = Roo.get(els.inner, true);
46453      this.textEl = Roo.bootstrap.version == 4 ?
46454         this.el : Roo.get(this.el.dom.firstChild, true);
46455
46456     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46457     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46458
46459     
46460 //    this.el.on("mousedown", this.onTabMouseDown, this);
46461     this.el.on("click", this.onTabClick, this);
46462     /** @private */
46463     if(config.closable){
46464         var c = Roo.get(els.close, true);
46465         c.dom.title = this.closeText;
46466         c.addClassOnOver("close-over");
46467         c.on("click", this.closeClick, this);
46468      }
46469
46470     this.addEvents({
46471          /**
46472          * @event activate
46473          * Fires when this tab becomes the active tab.
46474          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46475          * @param {Roo.TabPanelItem} this
46476          */
46477         "activate": true,
46478         /**
46479          * @event beforeclose
46480          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46481          * @param {Roo.TabPanelItem} this
46482          * @param {Object} e Set cancel to true on this object to cancel the close.
46483          */
46484         "beforeclose": true,
46485         /**
46486          * @event close
46487          * Fires when this tab is closed.
46488          * @param {Roo.TabPanelItem} this
46489          */
46490          "close": true,
46491         /**
46492          * @event deactivate
46493          * Fires when this tab is no longer the active tab.
46494          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46495          * @param {Roo.TabPanelItem} this
46496          */
46497          "deactivate" : true
46498     });
46499     this.hidden = false;
46500
46501     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46502 };
46503
46504 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46505            {
46506     purgeListeners : function(){
46507        Roo.util.Observable.prototype.purgeListeners.call(this);
46508        this.el.removeAllListeners();
46509     },
46510     /**
46511      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46512      */
46513     show : function(){
46514         this.status_node.addClass("active");
46515         this.showAction();
46516         if(Roo.isOpera){
46517             this.tabPanel.stripWrap.repaint();
46518         }
46519         this.fireEvent("activate", this.tabPanel, this);
46520     },
46521
46522     /**
46523      * Returns true if this tab is the active tab.
46524      * @return {Boolean}
46525      */
46526     isActive : function(){
46527         return this.tabPanel.getActiveTab() == this;
46528     },
46529
46530     /**
46531      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46532      */
46533     hide : function(){
46534         this.status_node.removeClass("active");
46535         this.hideAction();
46536         this.fireEvent("deactivate", this.tabPanel, this);
46537     },
46538
46539     hideAction : function(){
46540         this.bodyEl.hide();
46541         this.bodyEl.setStyle("position", "absolute");
46542         this.bodyEl.setLeft("-20000px");
46543         this.bodyEl.setTop("-20000px");
46544     },
46545
46546     showAction : function(){
46547         this.bodyEl.setStyle("position", "relative");
46548         this.bodyEl.setTop("");
46549         this.bodyEl.setLeft("");
46550         this.bodyEl.show();
46551     },
46552
46553     /**
46554      * Set the tooltip for the tab.
46555      * @param {String} tooltip The tab's tooltip
46556      */
46557     setTooltip : function(text){
46558         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46559             this.textEl.dom.qtip = text;
46560             this.textEl.dom.removeAttribute('title');
46561         }else{
46562             this.textEl.dom.title = text;
46563         }
46564     },
46565
46566     onTabClick : function(e){
46567         e.preventDefault();
46568         this.tabPanel.activate(this.id);
46569     },
46570
46571     onTabMouseDown : function(e){
46572         e.preventDefault();
46573         this.tabPanel.activate(this.id);
46574     },
46575 /*
46576     getWidth : function(){
46577         return this.inner.getWidth();
46578     },
46579
46580     setWidth : function(width){
46581         var iwidth = width - this.linode.getPadding("lr");
46582         this.inner.setWidth(iwidth);
46583         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46584         this.linode.setWidth(width);
46585     },
46586 */
46587     /**
46588      * Show or hide the tab
46589      * @param {Boolean} hidden True to hide or false to show.
46590      */
46591     setHidden : function(hidden){
46592         this.hidden = hidden;
46593         this.linode.setStyle("display", hidden ? "none" : "");
46594     },
46595
46596     /**
46597      * Returns true if this tab is "hidden"
46598      * @return {Boolean}
46599      */
46600     isHidden : function(){
46601         return this.hidden;
46602     },
46603
46604     /**
46605      * Returns the text for this tab
46606      * @return {String}
46607      */
46608     getText : function(){
46609         return this.text;
46610     },
46611     /*
46612     autoSize : function(){
46613         //this.el.beginMeasure();
46614         this.textEl.setWidth(1);
46615         /*
46616          *  #2804 [new] Tabs in Roojs
46617          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46618          */
46619         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46620         //this.el.endMeasure();
46621     //},
46622
46623     /**
46624      * Sets the text for the tab (Note: this also sets the tooltip text)
46625      * @param {String} text The tab's text and tooltip
46626      */
46627     setText : function(text){
46628         this.text = text;
46629         this.textEl.update(text);
46630         this.setTooltip(text);
46631         //if(!this.tabPanel.resizeTabs){
46632         //    this.autoSize();
46633         //}
46634     },
46635     /**
46636      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46637      */
46638     activate : function(){
46639         this.tabPanel.activate(this.id);
46640     },
46641
46642     /**
46643      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46644      */
46645     disable : function(){
46646         if(this.tabPanel.active != this){
46647             this.disabled = true;
46648             this.status_node.addClass("disabled");
46649         }
46650     },
46651
46652     /**
46653      * Enables this TabPanelItem if it was previously disabled.
46654      */
46655     enable : function(){
46656         this.disabled = false;
46657         this.status_node.removeClass("disabled");
46658     },
46659
46660     /**
46661      * Sets the content for this TabPanelItem.
46662      * @param {String} content The content
46663      * @param {Boolean} loadScripts true to look for and load scripts
46664      */
46665     setContent : function(content, loadScripts){
46666         this.bodyEl.update(content, loadScripts);
46667     },
46668
46669     /**
46670      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46671      * @return {Roo.UpdateManager} The UpdateManager
46672      */
46673     getUpdateManager : function(){
46674         return this.bodyEl.getUpdateManager();
46675     },
46676
46677     /**
46678      * Set a URL to be used to load the content for this TabPanelItem.
46679      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46680      * @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)
46681      * @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)
46682      * @return {Roo.UpdateManager} The UpdateManager
46683      */
46684     setUrl : function(url, params, loadOnce){
46685         if(this.refreshDelegate){
46686             this.un('activate', this.refreshDelegate);
46687         }
46688         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46689         this.on("activate", this.refreshDelegate);
46690         return this.bodyEl.getUpdateManager();
46691     },
46692
46693     /** @private */
46694     _handleRefresh : function(url, params, loadOnce){
46695         if(!loadOnce || !this.loaded){
46696             var updater = this.bodyEl.getUpdateManager();
46697             updater.update(url, params, this._setLoaded.createDelegate(this));
46698         }
46699     },
46700
46701     /**
46702      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
46703      *   Will fail silently if the setUrl method has not been called.
46704      *   This does not activate the panel, just updates its content.
46705      */
46706     refresh : function(){
46707         if(this.refreshDelegate){
46708            this.loaded = false;
46709            this.refreshDelegate();
46710         }
46711     },
46712
46713     /** @private */
46714     _setLoaded : function(){
46715         this.loaded = true;
46716     },
46717
46718     /** @private */
46719     closeClick : function(e){
46720         var o = {};
46721         e.stopEvent();
46722         this.fireEvent("beforeclose", this, o);
46723         if(o.cancel !== true){
46724             this.tabPanel.removeTab(this.id);
46725         }
46726     },
46727     /**
46728      * The text displayed in the tooltip for the close icon.
46729      * @type String
46730      */
46731     closeText : "Close this tab"
46732 });
46733 /**
46734 *    This script refer to:
46735 *    Title: International Telephone Input
46736 *    Author: Jack O'Connor
46737 *    Code version:  v12.1.12
46738 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46739 **/
46740
46741 Roo.bootstrap.form.PhoneInputData = function() {
46742     var d = [
46743       [
46744         "Afghanistan (‫افغانستان‬‎)",
46745         "af",
46746         "93"
46747       ],
46748       [
46749         "Albania (Shqipëri)",
46750         "al",
46751         "355"
46752       ],
46753       [
46754         "Algeria (‫الجزائر‬‎)",
46755         "dz",
46756         "213"
46757       ],
46758       [
46759         "American Samoa",
46760         "as",
46761         "1684"
46762       ],
46763       [
46764         "Andorra",
46765         "ad",
46766         "376"
46767       ],
46768       [
46769         "Angola",
46770         "ao",
46771         "244"
46772       ],
46773       [
46774         "Anguilla",
46775         "ai",
46776         "1264"
46777       ],
46778       [
46779         "Antigua and Barbuda",
46780         "ag",
46781         "1268"
46782       ],
46783       [
46784         "Argentina",
46785         "ar",
46786         "54"
46787       ],
46788       [
46789         "Armenia (Հայաստան)",
46790         "am",
46791         "374"
46792       ],
46793       [
46794         "Aruba",
46795         "aw",
46796         "297"
46797       ],
46798       [
46799         "Australia",
46800         "au",
46801         "61",
46802         0
46803       ],
46804       [
46805         "Austria (Österreich)",
46806         "at",
46807         "43"
46808       ],
46809       [
46810         "Azerbaijan (Azərbaycan)",
46811         "az",
46812         "994"
46813       ],
46814       [
46815         "Bahamas",
46816         "bs",
46817         "1242"
46818       ],
46819       [
46820         "Bahrain (‫البحرين‬‎)",
46821         "bh",
46822         "973"
46823       ],
46824       [
46825         "Bangladesh (বাংলাদেশ)",
46826         "bd",
46827         "880"
46828       ],
46829       [
46830         "Barbados",
46831         "bb",
46832         "1246"
46833       ],
46834       [
46835         "Belarus (Беларусь)",
46836         "by",
46837         "375"
46838       ],
46839       [
46840         "Belgium (België)",
46841         "be",
46842         "32"
46843       ],
46844       [
46845         "Belize",
46846         "bz",
46847         "501"
46848       ],
46849       [
46850         "Benin (Bénin)",
46851         "bj",
46852         "229"
46853       ],
46854       [
46855         "Bermuda",
46856         "bm",
46857         "1441"
46858       ],
46859       [
46860         "Bhutan (འབྲུག)",
46861         "bt",
46862         "975"
46863       ],
46864       [
46865         "Bolivia",
46866         "bo",
46867         "591"
46868       ],
46869       [
46870         "Bosnia and Herzegovina (Босна и Херцеговина)",
46871         "ba",
46872         "387"
46873       ],
46874       [
46875         "Botswana",
46876         "bw",
46877         "267"
46878       ],
46879       [
46880         "Brazil (Brasil)",
46881         "br",
46882         "55"
46883       ],
46884       [
46885         "British Indian Ocean Territory",
46886         "io",
46887         "246"
46888       ],
46889       [
46890         "British Virgin Islands",
46891         "vg",
46892         "1284"
46893       ],
46894       [
46895         "Brunei",
46896         "bn",
46897         "673"
46898       ],
46899       [
46900         "Bulgaria (България)",
46901         "bg",
46902         "359"
46903       ],
46904       [
46905         "Burkina Faso",
46906         "bf",
46907         "226"
46908       ],
46909       [
46910         "Burundi (Uburundi)",
46911         "bi",
46912         "257"
46913       ],
46914       [
46915         "Cambodia (កម្ពុជា)",
46916         "kh",
46917         "855"
46918       ],
46919       [
46920         "Cameroon (Cameroun)",
46921         "cm",
46922         "237"
46923       ],
46924       [
46925         "Canada",
46926         "ca",
46927         "1",
46928         1,
46929         ["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"]
46930       ],
46931       [
46932         "Cape Verde (Kabu Verdi)",
46933         "cv",
46934         "238"
46935       ],
46936       [
46937         "Caribbean Netherlands",
46938         "bq",
46939         "599",
46940         1
46941       ],
46942       [
46943         "Cayman Islands",
46944         "ky",
46945         "1345"
46946       ],
46947       [
46948         "Central African Republic (République centrafricaine)",
46949         "cf",
46950         "236"
46951       ],
46952       [
46953         "Chad (Tchad)",
46954         "td",
46955         "235"
46956       ],
46957       [
46958         "Chile",
46959         "cl",
46960         "56"
46961       ],
46962       [
46963         "China (中国)",
46964         "cn",
46965         "86"
46966       ],
46967       [
46968         "Christmas Island",
46969         "cx",
46970         "61",
46971         2
46972       ],
46973       [
46974         "Cocos (Keeling) Islands",
46975         "cc",
46976         "61",
46977         1
46978       ],
46979       [
46980         "Colombia",
46981         "co",
46982         "57"
46983       ],
46984       [
46985         "Comoros (‫جزر القمر‬‎)",
46986         "km",
46987         "269"
46988       ],
46989       [
46990         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
46991         "cd",
46992         "243"
46993       ],
46994       [
46995         "Congo (Republic) (Congo-Brazzaville)",
46996         "cg",
46997         "242"
46998       ],
46999       [
47000         "Cook Islands",
47001         "ck",
47002         "682"
47003       ],
47004       [
47005         "Costa Rica",
47006         "cr",
47007         "506"
47008       ],
47009       [
47010         "Côte d’Ivoire",
47011         "ci",
47012         "225"
47013       ],
47014       [
47015         "Croatia (Hrvatska)",
47016         "hr",
47017         "385"
47018       ],
47019       [
47020         "Cuba",
47021         "cu",
47022         "53"
47023       ],
47024       [
47025         "Curaçao",
47026         "cw",
47027         "599",
47028         0
47029       ],
47030       [
47031         "Cyprus (Κύπρος)",
47032         "cy",
47033         "357"
47034       ],
47035       [
47036         "Czech Republic (Česká republika)",
47037         "cz",
47038         "420"
47039       ],
47040       [
47041         "Denmark (Danmark)",
47042         "dk",
47043         "45"
47044       ],
47045       [
47046         "Djibouti",
47047         "dj",
47048         "253"
47049       ],
47050       [
47051         "Dominica",
47052         "dm",
47053         "1767"
47054       ],
47055       [
47056         "Dominican Republic (República Dominicana)",
47057         "do",
47058         "1",
47059         2,
47060         ["809", "829", "849"]
47061       ],
47062       [
47063         "Ecuador",
47064         "ec",
47065         "593"
47066       ],
47067       [
47068         "Egypt (‫مصر‬‎)",
47069         "eg",
47070         "20"
47071       ],
47072       [
47073         "El Salvador",
47074         "sv",
47075         "503"
47076       ],
47077       [
47078         "Equatorial Guinea (Guinea Ecuatorial)",
47079         "gq",
47080         "240"
47081       ],
47082       [
47083         "Eritrea",
47084         "er",
47085         "291"
47086       ],
47087       [
47088         "Estonia (Eesti)",
47089         "ee",
47090         "372"
47091       ],
47092       [
47093         "Ethiopia",
47094         "et",
47095         "251"
47096       ],
47097       [
47098         "Falkland Islands (Islas Malvinas)",
47099         "fk",
47100         "500"
47101       ],
47102       [
47103         "Faroe Islands (Føroyar)",
47104         "fo",
47105         "298"
47106       ],
47107       [
47108         "Fiji",
47109         "fj",
47110         "679"
47111       ],
47112       [
47113         "Finland (Suomi)",
47114         "fi",
47115         "358",
47116         0
47117       ],
47118       [
47119         "France",
47120         "fr",
47121         "33"
47122       ],
47123       [
47124         "French Guiana (Guyane française)",
47125         "gf",
47126         "594"
47127       ],
47128       [
47129         "French Polynesia (Polynésie française)",
47130         "pf",
47131         "689"
47132       ],
47133       [
47134         "Gabon",
47135         "ga",
47136         "241"
47137       ],
47138       [
47139         "Gambia",
47140         "gm",
47141         "220"
47142       ],
47143       [
47144         "Georgia (საქართველო)",
47145         "ge",
47146         "995"
47147       ],
47148       [
47149         "Germany (Deutschland)",
47150         "de",
47151         "49"
47152       ],
47153       [
47154         "Ghana (Gaana)",
47155         "gh",
47156         "233"
47157       ],
47158       [
47159         "Gibraltar",
47160         "gi",
47161         "350"
47162       ],
47163       [
47164         "Greece (Ελλάδα)",
47165         "gr",
47166         "30"
47167       ],
47168       [
47169         "Greenland (Kalaallit Nunaat)",
47170         "gl",
47171         "299"
47172       ],
47173       [
47174         "Grenada",
47175         "gd",
47176         "1473"
47177       ],
47178       [
47179         "Guadeloupe",
47180         "gp",
47181         "590",
47182         0
47183       ],
47184       [
47185         "Guam",
47186         "gu",
47187         "1671"
47188       ],
47189       [
47190         "Guatemala",
47191         "gt",
47192         "502"
47193       ],
47194       [
47195         "Guernsey",
47196         "gg",
47197         "44",
47198         1
47199       ],
47200       [
47201         "Guinea (Guinée)",
47202         "gn",
47203         "224"
47204       ],
47205       [
47206         "Guinea-Bissau (Guiné Bissau)",
47207         "gw",
47208         "245"
47209       ],
47210       [
47211         "Guyana",
47212         "gy",
47213         "592"
47214       ],
47215       [
47216         "Haiti",
47217         "ht",
47218         "509"
47219       ],
47220       [
47221         "Honduras",
47222         "hn",
47223         "504"
47224       ],
47225       [
47226         "Hong Kong (香港)",
47227         "hk",
47228         "852"
47229       ],
47230       [
47231         "Hungary (Magyarország)",
47232         "hu",
47233         "36"
47234       ],
47235       [
47236         "Iceland (Ísland)",
47237         "is",
47238         "354"
47239       ],
47240       [
47241         "India (भारत)",
47242         "in",
47243         "91"
47244       ],
47245       [
47246         "Indonesia",
47247         "id",
47248         "62"
47249       ],
47250       [
47251         "Iran (‫ایران‬‎)",
47252         "ir",
47253         "98"
47254       ],
47255       [
47256         "Iraq (‫العراق‬‎)",
47257         "iq",
47258         "964"
47259       ],
47260       [
47261         "Ireland",
47262         "ie",
47263         "353"
47264       ],
47265       [
47266         "Isle of Man",
47267         "im",
47268         "44",
47269         2
47270       ],
47271       [
47272         "Israel (‫ישראל‬‎)",
47273         "il",
47274         "972"
47275       ],
47276       [
47277         "Italy (Italia)",
47278         "it",
47279         "39",
47280         0
47281       ],
47282       [
47283         "Jamaica",
47284         "jm",
47285         "1876"
47286       ],
47287       [
47288         "Japan (日本)",
47289         "jp",
47290         "81"
47291       ],
47292       [
47293         "Jersey",
47294         "je",
47295         "44",
47296         3
47297       ],
47298       [
47299         "Jordan (‫الأردن‬‎)",
47300         "jo",
47301         "962"
47302       ],
47303       [
47304         "Kazakhstan (Казахстан)",
47305         "kz",
47306         "7",
47307         1
47308       ],
47309       [
47310         "Kenya",
47311         "ke",
47312         "254"
47313       ],
47314       [
47315         "Kiribati",
47316         "ki",
47317         "686"
47318       ],
47319       [
47320         "Kosovo",
47321         "xk",
47322         "383"
47323       ],
47324       [
47325         "Kuwait (‫الكويت‬‎)",
47326         "kw",
47327         "965"
47328       ],
47329       [
47330         "Kyrgyzstan (Кыргызстан)",
47331         "kg",
47332         "996"
47333       ],
47334       [
47335         "Laos (ລາວ)",
47336         "la",
47337         "856"
47338       ],
47339       [
47340         "Latvia (Latvija)",
47341         "lv",
47342         "371"
47343       ],
47344       [
47345         "Lebanon (‫لبنان‬‎)",
47346         "lb",
47347         "961"
47348       ],
47349       [
47350         "Lesotho",
47351         "ls",
47352         "266"
47353       ],
47354       [
47355         "Liberia",
47356         "lr",
47357         "231"
47358       ],
47359       [
47360         "Libya (‫ليبيا‬‎)",
47361         "ly",
47362         "218"
47363       ],
47364       [
47365         "Liechtenstein",
47366         "li",
47367         "423"
47368       ],
47369       [
47370         "Lithuania (Lietuva)",
47371         "lt",
47372         "370"
47373       ],
47374       [
47375         "Luxembourg",
47376         "lu",
47377         "352"
47378       ],
47379       [
47380         "Macau (澳門)",
47381         "mo",
47382         "853"
47383       ],
47384       [
47385         "Macedonia (FYROM) (Македонија)",
47386         "mk",
47387         "389"
47388       ],
47389       [
47390         "Madagascar (Madagasikara)",
47391         "mg",
47392         "261"
47393       ],
47394       [
47395         "Malawi",
47396         "mw",
47397         "265"
47398       ],
47399       [
47400         "Malaysia",
47401         "my",
47402         "60"
47403       ],
47404       [
47405         "Maldives",
47406         "mv",
47407         "960"
47408       ],
47409       [
47410         "Mali",
47411         "ml",
47412         "223"
47413       ],
47414       [
47415         "Malta",
47416         "mt",
47417         "356"
47418       ],
47419       [
47420         "Marshall Islands",
47421         "mh",
47422         "692"
47423       ],
47424       [
47425         "Martinique",
47426         "mq",
47427         "596"
47428       ],
47429       [
47430         "Mauritania (‫موريتانيا‬‎)",
47431         "mr",
47432         "222"
47433       ],
47434       [
47435         "Mauritius (Moris)",
47436         "mu",
47437         "230"
47438       ],
47439       [
47440         "Mayotte",
47441         "yt",
47442         "262",
47443         1
47444       ],
47445       [
47446         "Mexico (México)",
47447         "mx",
47448         "52"
47449       ],
47450       [
47451         "Micronesia",
47452         "fm",
47453         "691"
47454       ],
47455       [
47456         "Moldova (Republica Moldova)",
47457         "md",
47458         "373"
47459       ],
47460       [
47461         "Monaco",
47462         "mc",
47463         "377"
47464       ],
47465       [
47466         "Mongolia (Монгол)",
47467         "mn",
47468         "976"
47469       ],
47470       [
47471         "Montenegro (Crna Gora)",
47472         "me",
47473         "382"
47474       ],
47475       [
47476         "Montserrat",
47477         "ms",
47478         "1664"
47479       ],
47480       [
47481         "Morocco (‫المغرب‬‎)",
47482         "ma",
47483         "212",
47484         0
47485       ],
47486       [
47487         "Mozambique (Moçambique)",
47488         "mz",
47489         "258"
47490       ],
47491       [
47492         "Myanmar (Burma) (မြန်မာ)",
47493         "mm",
47494         "95"
47495       ],
47496       [
47497         "Namibia (Namibië)",
47498         "na",
47499         "264"
47500       ],
47501       [
47502         "Nauru",
47503         "nr",
47504         "674"
47505       ],
47506       [
47507         "Nepal (नेपाल)",
47508         "np",
47509         "977"
47510       ],
47511       [
47512         "Netherlands (Nederland)",
47513         "nl",
47514         "31"
47515       ],
47516       [
47517         "New Caledonia (Nouvelle-Calédonie)",
47518         "nc",
47519         "687"
47520       ],
47521       [
47522         "New Zealand",
47523         "nz",
47524         "64"
47525       ],
47526       [
47527         "Nicaragua",
47528         "ni",
47529         "505"
47530       ],
47531       [
47532         "Niger (Nijar)",
47533         "ne",
47534         "227"
47535       ],
47536       [
47537         "Nigeria",
47538         "ng",
47539         "234"
47540       ],
47541       [
47542         "Niue",
47543         "nu",
47544         "683"
47545       ],
47546       [
47547         "Norfolk Island",
47548         "nf",
47549         "672"
47550       ],
47551       [
47552         "North Korea (조선 민주주의 인민 공화국)",
47553         "kp",
47554         "850"
47555       ],
47556       [
47557         "Northern Mariana Islands",
47558         "mp",
47559         "1670"
47560       ],
47561       [
47562         "Norway (Norge)",
47563         "no",
47564         "47",
47565         0
47566       ],
47567       [
47568         "Oman (‫عُمان‬‎)",
47569         "om",
47570         "968"
47571       ],
47572       [
47573         "Pakistan (‫پاکستان‬‎)",
47574         "pk",
47575         "92"
47576       ],
47577       [
47578         "Palau",
47579         "pw",
47580         "680"
47581       ],
47582       [
47583         "Palestine (‫فلسطين‬‎)",
47584         "ps",
47585         "970"
47586       ],
47587       [
47588         "Panama (Panamá)",
47589         "pa",
47590         "507"
47591       ],
47592       [
47593         "Papua New Guinea",
47594         "pg",
47595         "675"
47596       ],
47597       [
47598         "Paraguay",
47599         "py",
47600         "595"
47601       ],
47602       [
47603         "Peru (Perú)",
47604         "pe",
47605         "51"
47606       ],
47607       [
47608         "Philippines",
47609         "ph",
47610         "63"
47611       ],
47612       [
47613         "Poland (Polska)",
47614         "pl",
47615         "48"
47616       ],
47617       [
47618         "Portugal",
47619         "pt",
47620         "351"
47621       ],
47622       [
47623         "Puerto Rico",
47624         "pr",
47625         "1",
47626         3,
47627         ["787", "939"]
47628       ],
47629       [
47630         "Qatar (‫قطر‬‎)",
47631         "qa",
47632         "974"
47633       ],
47634       [
47635         "Réunion (La Réunion)",
47636         "re",
47637         "262",
47638         0
47639       ],
47640       [
47641         "Romania (România)",
47642         "ro",
47643         "40"
47644       ],
47645       [
47646         "Russia (Россия)",
47647         "ru",
47648         "7",
47649         0
47650       ],
47651       [
47652         "Rwanda",
47653         "rw",
47654         "250"
47655       ],
47656       [
47657         "Saint Barthélemy",
47658         "bl",
47659         "590",
47660         1
47661       ],
47662       [
47663         "Saint Helena",
47664         "sh",
47665         "290"
47666       ],
47667       [
47668         "Saint Kitts and Nevis",
47669         "kn",
47670         "1869"
47671       ],
47672       [
47673         "Saint Lucia",
47674         "lc",
47675         "1758"
47676       ],
47677       [
47678         "Saint Martin (Saint-Martin (partie française))",
47679         "mf",
47680         "590",
47681         2
47682       ],
47683       [
47684         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47685         "pm",
47686         "508"
47687       ],
47688       [
47689         "Saint Vincent and the Grenadines",
47690         "vc",
47691         "1784"
47692       ],
47693       [
47694         "Samoa",
47695         "ws",
47696         "685"
47697       ],
47698       [
47699         "San Marino",
47700         "sm",
47701         "378"
47702       ],
47703       [
47704         "São Tomé and Príncipe (São Tomé e Príncipe)",
47705         "st",
47706         "239"
47707       ],
47708       [
47709         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
47710         "sa",
47711         "966"
47712       ],
47713       [
47714         "Senegal (Sénégal)",
47715         "sn",
47716         "221"
47717       ],
47718       [
47719         "Serbia (Србија)",
47720         "rs",
47721         "381"
47722       ],
47723       [
47724         "Seychelles",
47725         "sc",
47726         "248"
47727       ],
47728       [
47729         "Sierra Leone",
47730         "sl",
47731         "232"
47732       ],
47733       [
47734         "Singapore",
47735         "sg",
47736         "65"
47737       ],
47738       [
47739         "Sint Maarten",
47740         "sx",
47741         "1721"
47742       ],
47743       [
47744         "Slovakia (Slovensko)",
47745         "sk",
47746         "421"
47747       ],
47748       [
47749         "Slovenia (Slovenija)",
47750         "si",
47751         "386"
47752       ],
47753       [
47754         "Solomon Islands",
47755         "sb",
47756         "677"
47757       ],
47758       [
47759         "Somalia (Soomaaliya)",
47760         "so",
47761         "252"
47762       ],
47763       [
47764         "South Africa",
47765         "za",
47766         "27"
47767       ],
47768       [
47769         "South Korea (대한민국)",
47770         "kr",
47771         "82"
47772       ],
47773       [
47774         "South Sudan (‫جنوب السودان‬‎)",
47775         "ss",
47776         "211"
47777       ],
47778       [
47779         "Spain (España)",
47780         "es",
47781         "34"
47782       ],
47783       [
47784         "Sri Lanka (ශ්‍රී ලංකාව)",
47785         "lk",
47786         "94"
47787       ],
47788       [
47789         "Sudan (‫السودان‬‎)",
47790         "sd",
47791         "249"
47792       ],
47793       [
47794         "Suriname",
47795         "sr",
47796         "597"
47797       ],
47798       [
47799         "Svalbard and Jan Mayen",
47800         "sj",
47801         "47",
47802         1
47803       ],
47804       [
47805         "Swaziland",
47806         "sz",
47807         "268"
47808       ],
47809       [
47810         "Sweden (Sverige)",
47811         "se",
47812         "46"
47813       ],
47814       [
47815         "Switzerland (Schweiz)",
47816         "ch",
47817         "41"
47818       ],
47819       [
47820         "Syria (‫سوريا‬‎)",
47821         "sy",
47822         "963"
47823       ],
47824       [
47825         "Taiwan (台灣)",
47826         "tw",
47827         "886"
47828       ],
47829       [
47830         "Tajikistan",
47831         "tj",
47832         "992"
47833       ],
47834       [
47835         "Tanzania",
47836         "tz",
47837         "255"
47838       ],
47839       [
47840         "Thailand (ไทย)",
47841         "th",
47842         "66"
47843       ],
47844       [
47845         "Timor-Leste",
47846         "tl",
47847         "670"
47848       ],
47849       [
47850         "Togo",
47851         "tg",
47852         "228"
47853       ],
47854       [
47855         "Tokelau",
47856         "tk",
47857         "690"
47858       ],
47859       [
47860         "Tonga",
47861         "to",
47862         "676"
47863       ],
47864       [
47865         "Trinidad and Tobago",
47866         "tt",
47867         "1868"
47868       ],
47869       [
47870         "Tunisia (‫تونس‬‎)",
47871         "tn",
47872         "216"
47873       ],
47874       [
47875         "Turkey (Türkiye)",
47876         "tr",
47877         "90"
47878       ],
47879       [
47880         "Turkmenistan",
47881         "tm",
47882         "993"
47883       ],
47884       [
47885         "Turks and Caicos Islands",
47886         "tc",
47887         "1649"
47888       ],
47889       [
47890         "Tuvalu",
47891         "tv",
47892         "688"
47893       ],
47894       [
47895         "U.S. Virgin Islands",
47896         "vi",
47897         "1340"
47898       ],
47899       [
47900         "Uganda",
47901         "ug",
47902         "256"
47903       ],
47904       [
47905         "Ukraine (Україна)",
47906         "ua",
47907         "380"
47908       ],
47909       [
47910         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
47911         "ae",
47912         "971"
47913       ],
47914       [
47915         "United Kingdom",
47916         "gb",
47917         "44",
47918         0
47919       ],
47920       [
47921         "United States",
47922         "us",
47923         "1",
47924         0
47925       ],
47926       [
47927         "Uruguay",
47928         "uy",
47929         "598"
47930       ],
47931       [
47932         "Uzbekistan (Oʻzbekiston)",
47933         "uz",
47934         "998"
47935       ],
47936       [
47937         "Vanuatu",
47938         "vu",
47939         "678"
47940       ],
47941       [
47942         "Vatican City (Città del Vaticano)",
47943         "va",
47944         "39",
47945         1
47946       ],
47947       [
47948         "Venezuela",
47949         "ve",
47950         "58"
47951       ],
47952       [
47953         "Vietnam (Việt Nam)",
47954         "vn",
47955         "84"
47956       ],
47957       [
47958         "Wallis and Futuna (Wallis-et-Futuna)",
47959         "wf",
47960         "681"
47961       ],
47962       [
47963         "Western Sahara (‫الصحراء الغربية‬‎)",
47964         "eh",
47965         "212",
47966         1
47967       ],
47968       [
47969         "Yemen (‫اليمن‬‎)",
47970         "ye",
47971         "967"
47972       ],
47973       [
47974         "Zambia",
47975         "zm",
47976         "260"
47977       ],
47978       [
47979         "Zimbabwe",
47980         "zw",
47981         "263"
47982       ],
47983       [
47984         "Åland Islands",
47985         "ax",
47986         "358",
47987         1
47988       ]
47989   ];
47990   
47991   return d;
47992 }/**
47993 *    This script refer to:
47994 *    Title: International Telephone Input
47995 *    Author: Jack O'Connor
47996 *    Code version:  v12.1.12
47997 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47998 **/
47999
48000 /**
48001  * @class Roo.bootstrap.form.PhoneInput
48002  * @extends Roo.bootstrap.form.TriggerField
48003  * An input with International dial-code selection
48004  
48005  * @cfg {String} defaultDialCode default '+852'
48006  * @cfg {Array} preferedCountries default []
48007   
48008  * @constructor
48009  * Create a new PhoneInput.
48010  * @param {Object} config Configuration options
48011  */
48012
48013 Roo.bootstrap.form.PhoneInput = function(config) {
48014     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48015 };
48016
48017 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48018         /**
48019         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48020         */
48021         listWidth: undefined,
48022         
48023         selectedClass: 'active',
48024         
48025         invalidClass : "has-warning",
48026         
48027         validClass: 'has-success',
48028         
48029         allowed: '0123456789',
48030         
48031         max_length: 15,
48032         
48033         /**
48034          * @cfg {String} defaultDialCode The default dial code when initializing the input
48035          */
48036         defaultDialCode: '+852',
48037         
48038         /**
48039          * @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
48040          */
48041         preferedCountries: false,
48042         
48043         getAutoCreate : function()
48044         {
48045             var data = Roo.bootstrap.form.PhoneInputData();
48046             var align = this.labelAlign || this.parentLabelAlign();
48047             var id = Roo.id();
48048             
48049             this.allCountries = [];
48050             this.dialCodeMapping = [];
48051             
48052             for (var i = 0; i < data.length; i++) {
48053               var c = data[i];
48054               this.allCountries[i] = {
48055                 name: c[0],
48056                 iso2: c[1],
48057                 dialCode: c[2],
48058                 priority: c[3] || 0,
48059                 areaCodes: c[4] || null
48060               };
48061               this.dialCodeMapping[c[2]] = {
48062                   name: c[0],
48063                   iso2: c[1],
48064                   priority: c[3] || 0,
48065                   areaCodes: c[4] || null
48066               };
48067             }
48068             
48069             var cfg = {
48070                 cls: 'form-group',
48071                 cn: []
48072             };
48073             
48074             var input =  {
48075                 tag: 'input',
48076                 id : id,
48077                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48078                 maxlength: this.max_length,
48079                 cls : 'form-control tel-input',
48080                 autocomplete: 'new-password'
48081             };
48082             
48083             var hiddenInput = {
48084                 tag: 'input',
48085                 type: 'hidden',
48086                 cls: 'hidden-tel-input'
48087             };
48088             
48089             if (this.name) {
48090                 hiddenInput.name = this.name;
48091             }
48092             
48093             if (this.disabled) {
48094                 input.disabled = true;
48095             }
48096             
48097             var flag_container = {
48098                 tag: 'div',
48099                 cls: 'flag-box',
48100                 cn: [
48101                     {
48102                         tag: 'div',
48103                         cls: 'flag'
48104                     },
48105                     {
48106                         tag: 'div',
48107                         cls: 'caret'
48108                     }
48109                 ]
48110             };
48111             
48112             var box = {
48113                 tag: 'div',
48114                 cls: this.hasFeedback ? 'has-feedback' : '',
48115                 cn: [
48116                     hiddenInput,
48117                     input,
48118                     {
48119                         tag: 'input',
48120                         cls: 'dial-code-holder',
48121                         disabled: true
48122                     }
48123                 ]
48124             };
48125             
48126             var container = {
48127                 cls: 'roo-select2-container input-group',
48128                 cn: [
48129                     flag_container,
48130                     box
48131                 ]
48132             };
48133             
48134             if (this.fieldLabel.length) {
48135                 var indicator = {
48136                     tag: 'i',
48137                     tooltip: 'This field is required'
48138                 };
48139                 
48140                 var label = {
48141                     tag: 'label',
48142                     'for':  id,
48143                     cls: 'control-label',
48144                     cn: []
48145                 };
48146                 
48147                 var label_text = {
48148                     tag: 'span',
48149                     html: this.fieldLabel
48150                 };
48151                 
48152                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48153                 label.cn = [
48154                     indicator,
48155                     label_text
48156                 ];
48157                 
48158                 if(this.indicatorpos == 'right') {
48159                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48160                     label.cn = [
48161                         label_text,
48162                         indicator
48163                     ];
48164                 }
48165                 
48166                 if(align == 'left') {
48167                     container = {
48168                         tag: 'div',
48169                         cn: [
48170                             container
48171                         ]
48172                     };
48173                     
48174                     if(this.labelWidth > 12){
48175                         label.style = "width: " + this.labelWidth + 'px';
48176                     }
48177                     if(this.labelWidth < 13 && this.labelmd == 0){
48178                         this.labelmd = this.labelWidth;
48179                     }
48180                     if(this.labellg > 0){
48181                         label.cls += ' col-lg-' + this.labellg;
48182                         input.cls += ' col-lg-' + (12 - this.labellg);
48183                     }
48184                     if(this.labelmd > 0){
48185                         label.cls += ' col-md-' + this.labelmd;
48186                         container.cls += ' col-md-' + (12 - this.labelmd);
48187                     }
48188                     if(this.labelsm > 0){
48189                         label.cls += ' col-sm-' + this.labelsm;
48190                         container.cls += ' col-sm-' + (12 - this.labelsm);
48191                     }
48192                     if(this.labelxs > 0){
48193                         label.cls += ' col-xs-' + this.labelxs;
48194                         container.cls += ' col-xs-' + (12 - this.labelxs);
48195                     }
48196                 }
48197             }
48198             
48199             cfg.cn = [
48200                 label,
48201                 container
48202             ];
48203             
48204             var settings = this;
48205             
48206             ['xs','sm','md','lg'].map(function(size){
48207                 if (settings[size]) {
48208                     cfg.cls += ' col-' + size + '-' + settings[size];
48209                 }
48210             });
48211             
48212             this.store = new Roo.data.Store({
48213                 proxy : new Roo.data.MemoryProxy({}),
48214                 reader : new Roo.data.JsonReader({
48215                     fields : [
48216                         {
48217                             'name' : 'name',
48218                             'type' : 'string'
48219                         },
48220                         {
48221                             'name' : 'iso2',
48222                             'type' : 'string'
48223                         },
48224                         {
48225                             'name' : 'dialCode',
48226                             'type' : 'string'
48227                         },
48228                         {
48229                             'name' : 'priority',
48230                             'type' : 'string'
48231                         },
48232                         {
48233                             'name' : 'areaCodes',
48234                             'type' : 'string'
48235                         }
48236                     ]
48237                 })
48238             });
48239             
48240             if(!this.preferedCountries) {
48241                 this.preferedCountries = [
48242                     'hk',
48243                     'gb',
48244                     'us'
48245                 ];
48246             }
48247             
48248             var p = this.preferedCountries.reverse();
48249             
48250             if(p) {
48251                 for (var i = 0; i < p.length; i++) {
48252                     for (var j = 0; j < this.allCountries.length; j++) {
48253                         if(this.allCountries[j].iso2 == p[i]) {
48254                             var t = this.allCountries[j];
48255                             this.allCountries.splice(j,1);
48256                             this.allCountries.unshift(t);
48257                         }
48258                     } 
48259                 }
48260             }
48261             
48262             this.store.proxy.data = {
48263                 success: true,
48264                 data: this.allCountries
48265             };
48266             
48267             return cfg;
48268         },
48269         
48270         initEvents : function()
48271         {
48272             this.createList();
48273             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48274             
48275             this.indicator = this.indicatorEl();
48276             this.flag = this.flagEl();
48277             this.dialCodeHolder = this.dialCodeHolderEl();
48278             
48279             this.trigger = this.el.select('div.flag-box',true).first();
48280             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48281             
48282             var _this = this;
48283             
48284             (function(){
48285                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48286                 _this.list.setWidth(lw);
48287             }).defer(100);
48288             
48289             this.list.on('mouseover', this.onViewOver, this);
48290             this.list.on('mousemove', this.onViewMove, this);
48291             this.inputEl().on("keyup", this.onKeyUp, this);
48292             this.inputEl().on("keypress", this.onKeyPress, this);
48293             
48294             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48295
48296             this.view = new Roo.View(this.list, this.tpl, {
48297                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48298             });
48299             
48300             this.view.on('click', this.onViewClick, this);
48301             this.setValue(this.defaultDialCode);
48302         },
48303         
48304         onTriggerClick : function(e)
48305         {
48306             Roo.log('trigger click');
48307             if(this.disabled){
48308                 return;
48309             }
48310             
48311             if(this.isExpanded()){
48312                 this.collapse();
48313                 this.hasFocus = false;
48314             }else {
48315                 this.store.load({});
48316                 this.hasFocus = true;
48317                 this.expand();
48318             }
48319         },
48320         
48321         isExpanded : function()
48322         {
48323             return this.list.isVisible();
48324         },
48325         
48326         collapse : function()
48327         {
48328             if(!this.isExpanded()){
48329                 return;
48330             }
48331             this.list.hide();
48332             Roo.get(document).un('mousedown', this.collapseIf, this);
48333             Roo.get(document).un('mousewheel', this.collapseIf, this);
48334             this.fireEvent('collapse', this);
48335             this.validate();
48336         },
48337         
48338         expand : function()
48339         {
48340             Roo.log('expand');
48341
48342             if(this.isExpanded() || !this.hasFocus){
48343                 return;
48344             }
48345             
48346             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48347             this.list.setWidth(lw);
48348             
48349             this.list.show();
48350             this.restrictHeight();
48351             
48352             Roo.get(document).on('mousedown', this.collapseIf, this);
48353             Roo.get(document).on('mousewheel', this.collapseIf, this);
48354             
48355             this.fireEvent('expand', this);
48356         },
48357         
48358         restrictHeight : function()
48359         {
48360             this.list.alignTo(this.inputEl(), this.listAlign);
48361             this.list.alignTo(this.inputEl(), this.listAlign);
48362         },
48363         
48364         onViewOver : function(e, t)
48365         {
48366             if(this.inKeyMode){
48367                 return;
48368             }
48369             var item = this.view.findItemFromChild(t);
48370             
48371             if(item){
48372                 var index = this.view.indexOf(item);
48373                 this.select(index, false);
48374             }
48375         },
48376
48377         // private
48378         onViewClick : function(view, doFocus, el, e)
48379         {
48380             var index = this.view.getSelectedIndexes()[0];
48381             
48382             var r = this.store.getAt(index);
48383             
48384             if(r){
48385                 this.onSelect(r, index);
48386             }
48387             if(doFocus !== false && !this.blockFocus){
48388                 this.inputEl().focus();
48389             }
48390         },
48391         
48392         onViewMove : function(e, t)
48393         {
48394             this.inKeyMode = false;
48395         },
48396         
48397         select : function(index, scrollIntoView)
48398         {
48399             this.selectedIndex = index;
48400             this.view.select(index);
48401             if(scrollIntoView !== false){
48402                 var el = this.view.getNode(index);
48403                 if(el){
48404                     this.list.scrollChildIntoView(el, false);
48405                 }
48406             }
48407         },
48408         
48409         createList : function()
48410         {
48411             this.list = Roo.get(document.body).createChild({
48412                 tag: 'ul',
48413                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48414                 style: 'display:none'
48415             });
48416             
48417             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48418         },
48419         
48420         collapseIf : function(e)
48421         {
48422             var in_combo  = e.within(this.el);
48423             var in_list =  e.within(this.list);
48424             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48425             
48426             if (in_combo || in_list || is_list) {
48427                 return;
48428             }
48429             this.collapse();
48430         },
48431         
48432         onSelect : function(record, index)
48433         {
48434             if(this.fireEvent('beforeselect', this, record, index) !== false){
48435                 
48436                 this.setFlagClass(record.data.iso2);
48437                 this.setDialCode(record.data.dialCode);
48438                 this.hasFocus = false;
48439                 this.collapse();
48440                 this.fireEvent('select', this, record, index);
48441             }
48442         },
48443         
48444         flagEl : function()
48445         {
48446             var flag = this.el.select('div.flag',true).first();
48447             if(!flag){
48448                 return false;
48449             }
48450             return flag;
48451         },
48452         
48453         dialCodeHolderEl : function()
48454         {
48455             var d = this.el.select('input.dial-code-holder',true).first();
48456             if(!d){
48457                 return false;
48458             }
48459             return d;
48460         },
48461         
48462         setDialCode : function(v)
48463         {
48464             this.dialCodeHolder.dom.value = '+'+v;
48465         },
48466         
48467         setFlagClass : function(n)
48468         {
48469             this.flag.dom.className = 'flag '+n;
48470         },
48471         
48472         getValue : function()
48473         {
48474             var v = this.inputEl().getValue();
48475             if(this.dialCodeHolder) {
48476                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48477             }
48478             return v;
48479         },
48480         
48481         setValue : function(v)
48482         {
48483             var d = this.getDialCode(v);
48484             
48485             //invalid dial code
48486             if(v.length == 0 || !d || d.length == 0) {
48487                 if(this.rendered){
48488                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48489                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48490                 }
48491                 return;
48492             }
48493             
48494             //valid dial code
48495             this.setFlagClass(this.dialCodeMapping[d].iso2);
48496             this.setDialCode(d);
48497             this.inputEl().dom.value = v.replace('+'+d,'');
48498             this.hiddenEl().dom.value = this.getValue();
48499             
48500             this.validate();
48501         },
48502         
48503         getDialCode : function(v)
48504         {
48505             v = v ||  '';
48506             
48507             if (v.length == 0) {
48508                 return this.dialCodeHolder.dom.value;
48509             }
48510             
48511             var dialCode = "";
48512             if (v.charAt(0) != "+") {
48513                 return false;
48514             }
48515             var numericChars = "";
48516             for (var i = 1; i < v.length; i++) {
48517               var c = v.charAt(i);
48518               if (!isNaN(c)) {
48519                 numericChars += c;
48520                 if (this.dialCodeMapping[numericChars]) {
48521                   dialCode = v.substr(1, i);
48522                 }
48523                 if (numericChars.length == 4) {
48524                   break;
48525                 }
48526               }
48527             }
48528             return dialCode;
48529         },
48530         
48531         reset : function()
48532         {
48533             this.setValue(this.defaultDialCode);
48534             this.validate();
48535         },
48536         
48537         hiddenEl : function()
48538         {
48539             return this.el.select('input.hidden-tel-input',true).first();
48540         },
48541         
48542         // after setting val
48543         onKeyUp : function(e){
48544             this.setValue(this.getValue());
48545         },
48546         
48547         onKeyPress : function(e){
48548             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48549                 e.stopEvent();
48550             }
48551         }
48552         
48553 });
48554 /**
48555  * @class Roo.bootstrap.form.MoneyField
48556  * @extends Roo.bootstrap.form.ComboBox
48557  * Bootstrap MoneyField class
48558  * 
48559  * @constructor
48560  * Create a new MoneyField.
48561  * @param {Object} config Configuration options
48562  */
48563
48564 Roo.bootstrap.form.MoneyField = function(config) {
48565     
48566     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48567     
48568 };
48569
48570 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48571     
48572     /**
48573      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48574      */
48575     allowDecimals : true,
48576     /**
48577      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48578      */
48579     decimalSeparator : ".",
48580     /**
48581      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48582      */
48583     decimalPrecision : 0,
48584     /**
48585      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48586      */
48587     allowNegative : true,
48588     /**
48589      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48590      */
48591     allowZero: true,
48592     /**
48593      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48594      */
48595     minValue : Number.NEGATIVE_INFINITY,
48596     /**
48597      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48598      */
48599     maxValue : Number.MAX_VALUE,
48600     /**
48601      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48602      */
48603     minText : "The minimum value for this field is {0}",
48604     /**
48605      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48606      */
48607     maxText : "The maximum value for this field is {0}",
48608     /**
48609      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48610      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48611      */
48612     nanText : "{0} is not a valid number",
48613     /**
48614      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48615      */
48616     castInt : true,
48617     /**
48618      * @cfg {String} defaults currency of the MoneyField
48619      * value should be in lkey
48620      */
48621     defaultCurrency : false,
48622     /**
48623      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48624      */
48625     thousandsDelimiter : false,
48626     /**
48627      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48628      */
48629     max_length: false,
48630     
48631     inputlg : 9,
48632     inputmd : 9,
48633     inputsm : 9,
48634     inputxs : 6,
48635      /**
48636      * @cfg {Roo.data.Store} store  Store to lookup currency??
48637      */
48638     store : false,
48639     
48640     getAutoCreate : function()
48641     {
48642         var align = this.labelAlign || this.parentLabelAlign();
48643         
48644         var id = Roo.id();
48645
48646         var cfg = {
48647             cls: 'form-group',
48648             cn: []
48649         };
48650
48651         var input =  {
48652             tag: 'input',
48653             id : id,
48654             cls : 'form-control roo-money-amount-input',
48655             autocomplete: 'new-password'
48656         };
48657         
48658         var hiddenInput = {
48659             tag: 'input',
48660             type: 'hidden',
48661             id: Roo.id(),
48662             cls: 'hidden-number-input'
48663         };
48664         
48665         if(this.max_length) {
48666             input.maxlength = this.max_length; 
48667         }
48668         
48669         if (this.name) {
48670             hiddenInput.name = this.name;
48671         }
48672
48673         if (this.disabled) {
48674             input.disabled = true;
48675         }
48676
48677         var clg = 12 - this.inputlg;
48678         var cmd = 12 - this.inputmd;
48679         var csm = 12 - this.inputsm;
48680         var cxs = 12 - this.inputxs;
48681         
48682         var container = {
48683             tag : 'div',
48684             cls : 'row roo-money-field',
48685             cn : [
48686                 {
48687                     tag : 'div',
48688                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48689                     cn : [
48690                         {
48691                             tag : 'div',
48692                             cls: 'roo-select2-container input-group',
48693                             cn: [
48694                                 {
48695                                     tag : 'input',
48696                                     cls : 'form-control roo-money-currency-input',
48697                                     autocomplete: 'new-password',
48698                                     readOnly : 1,
48699                                     name : this.currencyName
48700                                 },
48701                                 {
48702                                     tag :'span',
48703                                     cls : 'input-group-addon',
48704                                     cn : [
48705                                         {
48706                                             tag: 'span',
48707                                             cls: 'caret'
48708                                         }
48709                                     ]
48710                                 }
48711                             ]
48712                         }
48713                     ]
48714                 },
48715                 {
48716                     tag : 'div',
48717                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48718                     cn : [
48719                         {
48720                             tag: 'div',
48721                             cls: this.hasFeedback ? 'has-feedback' : '',
48722                             cn: [
48723                                 input
48724                             ]
48725                         }
48726                     ]
48727                 }
48728             ]
48729             
48730         };
48731         
48732         if (this.fieldLabel.length) {
48733             var indicator = {
48734                 tag: 'i',
48735                 tooltip: 'This field is required'
48736             };
48737
48738             var label = {
48739                 tag: 'label',
48740                 'for':  id,
48741                 cls: 'control-label',
48742                 cn: []
48743             };
48744
48745             var label_text = {
48746                 tag: 'span',
48747                 html: this.fieldLabel
48748             };
48749
48750             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48751             label.cn = [
48752                 indicator,
48753                 label_text
48754             ];
48755
48756             if(this.indicatorpos == 'right') {
48757                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48758                 label.cn = [
48759                     label_text,
48760                     indicator
48761                 ];
48762             }
48763
48764             if(align == 'left') {
48765                 container = {
48766                     tag: 'div',
48767                     cn: [
48768                         container
48769                     ]
48770                 };
48771
48772                 if(this.labelWidth > 12){
48773                     label.style = "width: " + this.labelWidth + 'px';
48774                 }
48775                 if(this.labelWidth < 13 && this.labelmd == 0){
48776                     this.labelmd = this.labelWidth;
48777                 }
48778                 if(this.labellg > 0){
48779                     label.cls += ' col-lg-' + this.labellg;
48780                     input.cls += ' col-lg-' + (12 - this.labellg);
48781                 }
48782                 if(this.labelmd > 0){
48783                     label.cls += ' col-md-' + this.labelmd;
48784                     container.cls += ' col-md-' + (12 - this.labelmd);
48785                 }
48786                 if(this.labelsm > 0){
48787                     label.cls += ' col-sm-' + this.labelsm;
48788                     container.cls += ' col-sm-' + (12 - this.labelsm);
48789                 }
48790                 if(this.labelxs > 0){
48791                     label.cls += ' col-xs-' + this.labelxs;
48792                     container.cls += ' col-xs-' + (12 - this.labelxs);
48793                 }
48794             }
48795         }
48796
48797         cfg.cn = [
48798             label,
48799             container,
48800             hiddenInput
48801         ];
48802         
48803         var settings = this;
48804
48805         ['xs','sm','md','lg'].map(function(size){
48806             if (settings[size]) {
48807                 cfg.cls += ' col-' + size + '-' + settings[size];
48808             }
48809         });
48810         
48811         return cfg;
48812     },
48813     
48814     initEvents : function()
48815     {
48816         this.indicator = this.indicatorEl();
48817         
48818         this.initCurrencyEvent();
48819         
48820         this.initNumberEvent();
48821     },
48822     
48823     initCurrencyEvent : function()
48824     {
48825         if (!this.store) {
48826             throw "can not find store for combo";
48827         }
48828         
48829         this.store = Roo.factory(this.store, Roo.data);
48830         this.store.parent = this;
48831         
48832         this.createList();
48833         
48834         this.triggerEl = this.el.select('.input-group-addon', true).first();
48835         
48836         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48837         
48838         var _this = this;
48839         
48840         (function(){
48841             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48842             _this.list.setWidth(lw);
48843         }).defer(100);
48844         
48845         this.list.on('mouseover', this.onViewOver, this);
48846         this.list.on('mousemove', this.onViewMove, this);
48847         this.list.on('scroll', this.onViewScroll, this);
48848         
48849         if(!this.tpl){
48850             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48851         }
48852         
48853         this.view = new Roo.View(this.list, this.tpl, {
48854             singleSelect:true, store: this.store, selectedClass: this.selectedClass
48855         });
48856         
48857         this.view.on('click', this.onViewClick, this);
48858         
48859         this.store.on('beforeload', this.onBeforeLoad, this);
48860         this.store.on('load', this.onLoad, this);
48861         this.store.on('loadexception', this.onLoadException, this);
48862         
48863         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48864             "up" : function(e){
48865                 this.inKeyMode = true;
48866                 this.selectPrev();
48867             },
48868
48869             "down" : function(e){
48870                 if(!this.isExpanded()){
48871                     this.onTriggerClick();
48872                 }else{
48873                     this.inKeyMode = true;
48874                     this.selectNext();
48875                 }
48876             },
48877
48878             "enter" : function(e){
48879                 this.collapse();
48880                 
48881                 if(this.fireEvent("specialkey", this, e)){
48882                     this.onViewClick(false);
48883                 }
48884                 
48885                 return true;
48886             },
48887
48888             "esc" : function(e){
48889                 this.collapse();
48890             },
48891
48892             "tab" : function(e){
48893                 this.collapse();
48894                 
48895                 if(this.fireEvent("specialkey", this, e)){
48896                     this.onViewClick(false);
48897                 }
48898                 
48899                 return true;
48900             },
48901
48902             scope : this,
48903
48904             doRelay : function(foo, bar, hname){
48905                 if(hname == 'down' || this.scope.isExpanded()){
48906                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48907                 }
48908                 return true;
48909             },
48910
48911             forceKeyDown: true
48912         });
48913         
48914         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48915         
48916     },
48917     
48918     initNumberEvent : function(e)
48919     {
48920         this.inputEl().on("keydown" , this.fireKey,  this);
48921         this.inputEl().on("focus", this.onFocus,  this);
48922         this.inputEl().on("blur", this.onBlur,  this);
48923         
48924         this.inputEl().relayEvent('keyup', this);
48925         
48926         if(this.indicator){
48927             this.indicator.addClass('invisible');
48928         }
48929  
48930         this.originalValue = this.getValue();
48931         
48932         if(this.validationEvent == 'keyup'){
48933             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48934             this.inputEl().on('keyup', this.filterValidation, this);
48935         }
48936         else if(this.validationEvent !== false){
48937             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48938         }
48939         
48940         if(this.selectOnFocus){
48941             this.on("focus", this.preFocus, this);
48942             
48943         }
48944         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48945             this.inputEl().on("keypress", this.filterKeys, this);
48946         } else {
48947             this.inputEl().relayEvent('keypress', this);
48948         }
48949         
48950         var allowed = "0123456789";
48951         
48952         if(this.allowDecimals){
48953             allowed += this.decimalSeparator;
48954         }
48955         
48956         if(this.allowNegative){
48957             allowed += "-";
48958         }
48959         
48960         if(this.thousandsDelimiter) {
48961             allowed += ",";
48962         }
48963         
48964         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48965         
48966         var keyPress = function(e){
48967             
48968             var k = e.getKey();
48969             
48970             var c = e.getCharCode();
48971             
48972             if(
48973                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48974                     allowed.indexOf(String.fromCharCode(c)) === -1
48975             ){
48976                 e.stopEvent();
48977                 return;
48978             }
48979             
48980             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
48981                 return;
48982             }
48983             
48984             if(allowed.indexOf(String.fromCharCode(c)) === -1){
48985                 e.stopEvent();
48986             }
48987         };
48988         
48989         this.inputEl().on("keypress", keyPress, this);
48990         
48991     },
48992     
48993     onTriggerClick : function(e)
48994     {   
48995         if(this.disabled){
48996             return;
48997         }
48998         
48999         this.page = 0;
49000         this.loadNext = false;
49001         
49002         if(this.isExpanded()){
49003             this.collapse();
49004             return;
49005         }
49006         
49007         this.hasFocus = true;
49008         
49009         if(this.triggerAction == 'all') {
49010             this.doQuery(this.allQuery, true);
49011             return;
49012         }
49013         
49014         this.doQuery(this.getRawValue());
49015     },
49016     
49017     getCurrency : function()
49018     {   
49019         var v = this.currencyEl().getValue();
49020         
49021         return v;
49022     },
49023     
49024     restrictHeight : function()
49025     {
49026         this.list.alignTo(this.currencyEl(), this.listAlign);
49027         this.list.alignTo(this.currencyEl(), this.listAlign);
49028     },
49029     
49030     onViewClick : function(view, doFocus, el, e)
49031     {
49032         var index = this.view.getSelectedIndexes()[0];
49033         
49034         var r = this.store.getAt(index);
49035         
49036         if(r){
49037             this.onSelect(r, index);
49038         }
49039     },
49040     
49041     onSelect : function(record, index){
49042         
49043         if(this.fireEvent('beforeselect', this, record, index) !== false){
49044         
49045             this.setFromCurrencyData(index > -1 ? record.data : false);
49046             
49047             this.collapse();
49048             
49049             this.fireEvent('select', this, record, index);
49050         }
49051     },
49052     
49053     setFromCurrencyData : function(o)
49054     {
49055         var currency = '';
49056         
49057         this.lastCurrency = o;
49058         
49059         if (this.currencyField) {
49060             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49061         } else {
49062             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
49063         }
49064         
49065         this.lastSelectionText = currency;
49066         
49067         //setting default currency
49068         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49069             this.setCurrency(this.defaultCurrency);
49070             return;
49071         }
49072         
49073         this.setCurrency(currency);
49074     },
49075     
49076     setFromData : function(o)
49077     {
49078         var c = {};
49079         
49080         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49081         
49082         this.setFromCurrencyData(c);
49083         
49084         var value = '';
49085         
49086         if (this.name) {
49087             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49088         } else {
49089             Roo.log('no value set for '+ (this.name ? this.name : this.id));
49090         }
49091         
49092         this.setValue(value);
49093         
49094     },
49095     
49096     setCurrency : function(v)
49097     {   
49098         this.currencyValue = v;
49099         
49100         if(this.rendered){
49101             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49102             this.validate();
49103         }
49104     },
49105     
49106     setValue : function(v)
49107     {
49108         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49109         
49110         this.value = v;
49111         
49112         if(this.rendered){
49113             
49114             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49115             
49116             this.inputEl().dom.value = (v == '') ? '' :
49117                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49118             
49119             if(!this.allowZero && v === '0') {
49120                 this.hiddenEl().dom.value = '';
49121                 this.inputEl().dom.value = '';
49122             }
49123             
49124             this.validate();
49125         }
49126     },
49127     
49128     getRawValue : function()
49129     {
49130         var v = this.inputEl().getValue();
49131         
49132         return v;
49133     },
49134     
49135     getValue : function()
49136     {
49137         return this.fixPrecision(this.parseValue(this.getRawValue()));
49138     },
49139     
49140     parseValue : function(value)
49141     {
49142         if(this.thousandsDelimiter) {
49143             value += "";
49144             r = new RegExp(",", "g");
49145             value = value.replace(r, "");
49146         }
49147         
49148         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49149         return isNaN(value) ? '' : value;
49150         
49151     },
49152     
49153     fixPrecision : function(value)
49154     {
49155         if(this.thousandsDelimiter) {
49156             value += "";
49157             r = new RegExp(",", "g");
49158             value = value.replace(r, "");
49159         }
49160         
49161         var nan = isNaN(value);
49162         
49163         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49164             return nan ? '' : value;
49165         }
49166         return parseFloat(value).toFixed(this.decimalPrecision);
49167     },
49168     
49169     decimalPrecisionFcn : function(v)
49170     {
49171         return Math.floor(v);
49172     },
49173     
49174     validateValue : function(value)
49175     {
49176         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49177             return false;
49178         }
49179         
49180         var num = this.parseValue(value);
49181         
49182         if(isNaN(num)){
49183             this.markInvalid(String.format(this.nanText, value));
49184             return false;
49185         }
49186         
49187         if(num < this.minValue){
49188             this.markInvalid(String.format(this.minText, this.minValue));
49189             return false;
49190         }
49191         
49192         if(num > this.maxValue){
49193             this.markInvalid(String.format(this.maxText, this.maxValue));
49194             return false;
49195         }
49196         
49197         return true;
49198     },
49199     
49200     validate : function()
49201     {
49202         if(this.disabled || this.allowBlank){
49203             this.markValid();
49204             return true;
49205         }
49206         
49207         var currency = this.getCurrency();
49208         
49209         if(this.validateValue(this.getRawValue()) && currency.length){
49210             this.markValid();
49211             return true;
49212         }
49213         
49214         this.markInvalid();
49215         return false;
49216     },
49217     
49218     getName: function()
49219     {
49220         return this.name;
49221     },
49222     
49223     beforeBlur : function()
49224     {
49225         if(!this.castInt){
49226             return;
49227         }
49228         
49229         var v = this.parseValue(this.getRawValue());
49230         
49231         if(v || v == 0){
49232             this.setValue(v);
49233         }
49234     },
49235     
49236     onBlur : function()
49237     {
49238         this.beforeBlur();
49239         
49240         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49241             //this.el.removeClass(this.focusClass);
49242         }
49243         
49244         this.hasFocus = false;
49245         
49246         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49247             this.validate();
49248         }
49249         
49250         var v = this.getValue();
49251         
49252         if(String(v) !== String(this.startValue)){
49253             this.fireEvent('change', this, v, this.startValue);
49254         }
49255         
49256         this.fireEvent("blur", this);
49257     },
49258     
49259     inputEl : function()
49260     {
49261         return this.el.select('.roo-money-amount-input', true).first();
49262     },
49263     
49264     currencyEl : function()
49265     {
49266         return this.el.select('.roo-money-currency-input', true).first();
49267     },
49268     
49269     hiddenEl : function()
49270     {
49271         return this.el.select('input.hidden-number-input',true).first();
49272     }
49273     
49274 });/**
49275  * @class Roo.bootstrap.BezierSignature
49276  * @extends Roo.bootstrap.Component
49277  * Bootstrap BezierSignature class
49278  * This script refer to:
49279  *    Title: Signature Pad
49280  *    Author: szimek
49281  *    Availability: https://github.com/szimek/signature_pad
49282  *
49283  * @constructor
49284  * Create a new BezierSignature
49285  * @param {Object} config The config object
49286  */
49287
49288 Roo.bootstrap.BezierSignature = function(config){
49289     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49290     this.addEvents({
49291         "resize" : true
49292     });
49293 };
49294
49295 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49296 {
49297      
49298     curve_data: [],
49299     
49300     is_empty: true,
49301     
49302     mouse_btn_down: true,
49303     
49304     /**
49305      * @cfg {int} canvas height
49306      */
49307     canvas_height: '200px',
49308     
49309     /**
49310      * @cfg {float|function} Radius of a single dot.
49311      */ 
49312     dot_size: false,
49313     
49314     /**
49315      * @cfg {float} Minimum width of a line. Defaults to 0.5.
49316      */
49317     min_width: 0.5,
49318     
49319     /**
49320      * @cfg {float} Maximum width of a line. Defaults to 2.5.
49321      */
49322     max_width: 2.5,
49323     
49324     /**
49325      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49326      */
49327     throttle: 16,
49328     
49329     /**
49330      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49331      */
49332     min_distance: 5,
49333     
49334     /**
49335      * @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.
49336      */
49337     bg_color: 'rgba(0, 0, 0, 0)',
49338     
49339     /**
49340      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49341      */
49342     dot_color: 'black',
49343     
49344     /**
49345      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49346      */ 
49347     velocity_filter_weight: 0.7,
49348     
49349     /**
49350      * @cfg {function} Callback when stroke begin. 
49351      */
49352     onBegin: false,
49353     
49354     /**
49355      * @cfg {function} Callback when stroke end.
49356      */
49357     onEnd: false,
49358     
49359     getAutoCreate : function()
49360     {
49361         var cls = 'roo-signature column';
49362         
49363         if(this.cls){
49364             cls += ' ' + this.cls;
49365         }
49366         
49367         var col_sizes = [
49368             'lg',
49369             'md',
49370             'sm',
49371             'xs'
49372         ];
49373         
49374         for(var i = 0; i < col_sizes.length; i++) {
49375             if(this[col_sizes[i]]) {
49376                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49377             }
49378         }
49379         
49380         var cfg = {
49381             tag: 'div',
49382             cls: cls,
49383             cn: [
49384                 {
49385                     tag: 'div',
49386                     cls: 'roo-signature-body',
49387                     cn: [
49388                         {
49389                             tag: 'canvas',
49390                             cls: 'roo-signature-body-canvas',
49391                             height: this.canvas_height,
49392                             width: this.canvas_width
49393                         }
49394                     ]
49395                 },
49396                 {
49397                     tag: 'input',
49398                     type: 'file',
49399                     style: 'display: none'
49400                 }
49401             ]
49402         };
49403         
49404         return cfg;
49405     },
49406     
49407     initEvents: function() 
49408     {
49409         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49410         
49411         var canvas = this.canvasEl();
49412         
49413         // mouse && touch event swapping...
49414         canvas.dom.style.touchAction = 'none';
49415         canvas.dom.style.msTouchAction = 'none';
49416         
49417         this.mouse_btn_down = false;
49418         canvas.on('mousedown', this._handleMouseDown, this);
49419         canvas.on('mousemove', this._handleMouseMove, this);
49420         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49421         
49422         if (window.PointerEvent) {
49423             canvas.on('pointerdown', this._handleMouseDown, this);
49424             canvas.on('pointermove', this._handleMouseMove, this);
49425             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49426         }
49427         
49428         if ('ontouchstart' in window) {
49429             canvas.on('touchstart', this._handleTouchStart, this);
49430             canvas.on('touchmove', this._handleTouchMove, this);
49431             canvas.on('touchend', this._handleTouchEnd, this);
49432         }
49433         
49434         Roo.EventManager.onWindowResize(this.resize, this, true);
49435         
49436         // file input event
49437         this.fileEl().on('change', this.uploadImage, this);
49438         
49439         this.clear();
49440         
49441         this.resize();
49442     },
49443     
49444     resize: function(){
49445         
49446         var canvas = this.canvasEl().dom;
49447         var ctx = this.canvasElCtx();
49448         var img_data = false;
49449         
49450         if(canvas.width > 0) {
49451             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49452         }
49453         // setting canvas width will clean img data
49454         canvas.width = 0;
49455         
49456         var style = window.getComputedStyle ? 
49457             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49458             
49459         var padding_left = parseInt(style.paddingLeft) || 0;
49460         var padding_right = parseInt(style.paddingRight) || 0;
49461         
49462         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49463         
49464         if(img_data) {
49465             ctx.putImageData(img_data, 0, 0);
49466         }
49467     },
49468     
49469     _handleMouseDown: function(e)
49470     {
49471         if (e.browserEvent.which === 1) {
49472             this.mouse_btn_down = true;
49473             this.strokeBegin(e);
49474         }
49475     },
49476     
49477     _handleMouseMove: function (e)
49478     {
49479         if (this.mouse_btn_down) {
49480             this.strokeMoveUpdate(e);
49481         }
49482     },
49483     
49484     _handleMouseUp: function (e)
49485     {
49486         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49487             this.mouse_btn_down = false;
49488             this.strokeEnd(e);
49489         }
49490     },
49491     
49492     _handleTouchStart: function (e) {
49493         
49494         e.preventDefault();
49495         if (e.browserEvent.targetTouches.length === 1) {
49496             // var touch = e.browserEvent.changedTouches[0];
49497             // this.strokeBegin(touch);
49498             
49499              this.strokeBegin(e); // assume e catching the correct xy...
49500         }
49501     },
49502     
49503     _handleTouchMove: function (e) {
49504         e.preventDefault();
49505         // var touch = event.targetTouches[0];
49506         // _this._strokeMoveUpdate(touch);
49507         this.strokeMoveUpdate(e);
49508     },
49509     
49510     _handleTouchEnd: function (e) {
49511         var wasCanvasTouched = e.target === this.canvasEl().dom;
49512         if (wasCanvasTouched) {
49513             e.preventDefault();
49514             // var touch = event.changedTouches[0];
49515             // _this._strokeEnd(touch);
49516             this.strokeEnd(e);
49517         }
49518     },
49519     
49520     reset: function () {
49521         this._lastPoints = [];
49522         this._lastVelocity = 0;
49523         this._lastWidth = (this.min_width + this.max_width) / 2;
49524         this.canvasElCtx().fillStyle = this.dot_color;
49525     },
49526     
49527     strokeMoveUpdate: function(e)
49528     {
49529         this.strokeUpdate(e);
49530         
49531         if (this.throttle) {
49532             this.throttleStroke(this.strokeUpdate, this.throttle);
49533         }
49534         else {
49535             this.strokeUpdate(e);
49536         }
49537     },
49538     
49539     strokeBegin: function(e)
49540     {
49541         var newPointGroup = {
49542             color: this.dot_color,
49543             points: []
49544         };
49545         
49546         if (typeof this.onBegin === 'function') {
49547             this.onBegin(e);
49548         }
49549         
49550         this.curve_data.push(newPointGroup);
49551         this.reset();
49552         this.strokeUpdate(e);
49553     },
49554     
49555     strokeUpdate: function(e)
49556     {
49557         var rect = this.canvasEl().dom.getBoundingClientRect();
49558         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49559         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49560         var lastPoints = lastPointGroup.points;
49561         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49562         var isLastPointTooClose = lastPoint
49563             ? point.distanceTo(lastPoint) <= this.min_distance
49564             : false;
49565         var color = lastPointGroup.color;
49566         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49567             var curve = this.addPoint(point);
49568             if (!lastPoint) {
49569                 this.drawDot({color: color, point: point});
49570             }
49571             else if (curve) {
49572                 this.drawCurve({color: color, curve: curve});
49573             }
49574             lastPoints.push({
49575                 time: point.time,
49576                 x: point.x,
49577                 y: point.y
49578             });
49579         }
49580     },
49581     
49582     strokeEnd: function(e)
49583     {
49584         this.strokeUpdate(e);
49585         if (typeof this.onEnd === 'function') {
49586             this.onEnd(e);
49587         }
49588     },
49589     
49590     addPoint:  function (point) {
49591         var _lastPoints = this._lastPoints;
49592         _lastPoints.push(point);
49593         if (_lastPoints.length > 2) {
49594             if (_lastPoints.length === 3) {
49595                 _lastPoints.unshift(_lastPoints[0]);
49596             }
49597             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49598             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49599             _lastPoints.shift();
49600             return curve;
49601         }
49602         return null;
49603     },
49604     
49605     calculateCurveWidths: function (startPoint, endPoint) {
49606         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49607             (1 - this.velocity_filter_weight) * this._lastVelocity;
49608
49609         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49610         var widths = {
49611             end: newWidth,
49612             start: this._lastWidth
49613         };
49614         
49615         this._lastVelocity = velocity;
49616         this._lastWidth = newWidth;
49617         return widths;
49618     },
49619     
49620     drawDot: function (_a) {
49621         var color = _a.color, point = _a.point;
49622         var ctx = this.canvasElCtx();
49623         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49624         ctx.beginPath();
49625         this.drawCurveSegment(point.x, point.y, width);
49626         ctx.closePath();
49627         ctx.fillStyle = color;
49628         ctx.fill();
49629     },
49630     
49631     drawCurve: function (_a) {
49632         var color = _a.color, curve = _a.curve;
49633         var ctx = this.canvasElCtx();
49634         var widthDelta = curve.endWidth - curve.startWidth;
49635         var drawSteps = Math.floor(curve.length()) * 2;
49636         ctx.beginPath();
49637         ctx.fillStyle = color;
49638         for (var i = 0; i < drawSteps; i += 1) {
49639         var t = i / drawSteps;
49640         var tt = t * t;
49641         var ttt = tt * t;
49642         var u = 1 - t;
49643         var uu = u * u;
49644         var uuu = uu * u;
49645         var x = uuu * curve.startPoint.x;
49646         x += 3 * uu * t * curve.control1.x;
49647         x += 3 * u * tt * curve.control2.x;
49648         x += ttt * curve.endPoint.x;
49649         var y = uuu * curve.startPoint.y;
49650         y += 3 * uu * t * curve.control1.y;
49651         y += 3 * u * tt * curve.control2.y;
49652         y += ttt * curve.endPoint.y;
49653         var width = curve.startWidth + ttt * widthDelta;
49654         this.drawCurveSegment(x, y, width);
49655         }
49656         ctx.closePath();
49657         ctx.fill();
49658     },
49659     
49660     drawCurveSegment: function (x, y, width) {
49661         var ctx = this.canvasElCtx();
49662         ctx.moveTo(x, y);
49663         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49664         this.is_empty = false;
49665     },
49666     
49667     clear: function()
49668     {
49669         var ctx = this.canvasElCtx();
49670         var canvas = this.canvasEl().dom;
49671         ctx.fillStyle = this.bg_color;
49672         ctx.clearRect(0, 0, canvas.width, canvas.height);
49673         ctx.fillRect(0, 0, canvas.width, canvas.height);
49674         this.curve_data = [];
49675         this.reset();
49676         this.is_empty = true;
49677     },
49678     
49679     fileEl: function()
49680     {
49681         return  this.el.select('input',true).first();
49682     },
49683     
49684     canvasEl: function()
49685     {
49686         return this.el.select('canvas',true).first();
49687     },
49688     
49689     canvasElCtx: function()
49690     {
49691         return this.el.select('canvas',true).first().dom.getContext('2d');
49692     },
49693     
49694     getImage: function(type)
49695     {
49696         if(this.is_empty) {
49697             return false;
49698         }
49699         
49700         // encryption ?
49701         return this.canvasEl().dom.toDataURL('image/'+type, 1);
49702     },
49703     
49704     drawFromImage: function(img_src)
49705     {
49706         var img = new Image();
49707         
49708         img.onload = function(){
49709             this.canvasElCtx().drawImage(img, 0, 0);
49710         }.bind(this);
49711         
49712         img.src = img_src;
49713         
49714         this.is_empty = false;
49715     },
49716     
49717     selectImage: function()
49718     {
49719         this.fileEl().dom.click();
49720     },
49721     
49722     uploadImage: function(e)
49723     {
49724         var reader = new FileReader();
49725         
49726         reader.onload = function(e){
49727             var img = new Image();
49728             img.onload = function(){
49729                 this.reset();
49730                 this.canvasElCtx().drawImage(img, 0, 0);
49731             }.bind(this);
49732             img.src = e.target.result;
49733         }.bind(this);
49734         
49735         reader.readAsDataURL(e.target.files[0]);
49736     },
49737     
49738     // Bezier Point Constructor
49739     Point: (function () {
49740         function Point(x, y, time) {
49741             this.x = x;
49742             this.y = y;
49743             this.time = time || Date.now();
49744         }
49745         Point.prototype.distanceTo = function (start) {
49746             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49747         };
49748         Point.prototype.equals = function (other) {
49749             return this.x === other.x && this.y === other.y && this.time === other.time;
49750         };
49751         Point.prototype.velocityFrom = function (start) {
49752             return this.time !== start.time
49753             ? this.distanceTo(start) / (this.time - start.time)
49754             : 0;
49755         };
49756         return Point;
49757     }()),
49758     
49759     
49760     // Bezier Constructor
49761     Bezier: (function () {
49762         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49763             this.startPoint = startPoint;
49764             this.control2 = control2;
49765             this.control1 = control1;
49766             this.endPoint = endPoint;
49767             this.startWidth = startWidth;
49768             this.endWidth = endWidth;
49769         }
49770         Bezier.fromPoints = function (points, widths, scope) {
49771             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49772             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49773             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49774         };
49775         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49776             var dx1 = s1.x - s2.x;
49777             var dy1 = s1.y - s2.y;
49778             var dx2 = s2.x - s3.x;
49779             var dy2 = s2.y - s3.y;
49780             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49781             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49782             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49783             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49784             var dxm = m1.x - m2.x;
49785             var dym = m1.y - m2.y;
49786             var k = l2 / (l1 + l2);
49787             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49788             var tx = s2.x - cm.x;
49789             var ty = s2.y - cm.y;
49790             return {
49791                 c1: new scope.Point(m1.x + tx, m1.y + ty),
49792                 c2: new scope.Point(m2.x + tx, m2.y + ty)
49793             };
49794         };
49795         Bezier.prototype.length = function () {
49796             var steps = 10;
49797             var length = 0;
49798             var px;
49799             var py;
49800             for (var i = 0; i <= steps; i += 1) {
49801                 var t = i / steps;
49802                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49803                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49804                 if (i > 0) {
49805                     var xdiff = cx - px;
49806                     var ydiff = cy - py;
49807                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49808                 }
49809                 px = cx;
49810                 py = cy;
49811             }
49812             return length;
49813         };
49814         Bezier.prototype.point = function (t, start, c1, c2, end) {
49815             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49816             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49817             + (3.0 * c2 * (1.0 - t) * t * t)
49818             + (end * t * t * t);
49819         };
49820         return Bezier;
49821     }()),
49822     
49823     throttleStroke: function(fn, wait) {
49824       if (wait === void 0) { wait = 250; }
49825       var previous = 0;
49826       var timeout = null;
49827       var result;
49828       var storedContext;
49829       var storedArgs;
49830       var later = function () {
49831           previous = Date.now();
49832           timeout = null;
49833           result = fn.apply(storedContext, storedArgs);
49834           if (!timeout) {
49835               storedContext = null;
49836               storedArgs = [];
49837           }
49838       };
49839       return function wrapper() {
49840           var args = [];
49841           for (var _i = 0; _i < arguments.length; _i++) {
49842               args[_i] = arguments[_i];
49843           }
49844           var now = Date.now();
49845           var remaining = wait - (now - previous);
49846           storedContext = this;
49847           storedArgs = args;
49848           if (remaining <= 0 || remaining > wait) {
49849               if (timeout) {
49850                   clearTimeout(timeout);
49851                   timeout = null;
49852               }
49853               previous = now;
49854               result = fn.apply(storedContext, storedArgs);
49855               if (!timeout) {
49856                   storedContext = null;
49857                   storedArgs = [];
49858               }
49859           }
49860           else if (!timeout) {
49861               timeout = window.setTimeout(later, remaining);
49862           }
49863           return result;
49864       };
49865   }
49866   
49867 });
49868
49869  
49870
49871  // old names for form elements
49872 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
49873 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
49874 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
49875 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
49876 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
49877 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
49878 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
49879 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
49880 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
49881 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
49882 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
49883 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
49884 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
49885 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
49886 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
49887 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
49888 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
49889 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
49890 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
49891 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
49892 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
49893 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
49894 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
49895 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
49896 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
49897 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
49898
49899 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
49900 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49901
49902 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
49903 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
49904
49905 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
49906 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49907 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
49908 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
49909