supporting multiple bullet point styles
[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.call(config, d.data[cm.getDataIndex(i)], false, d);
10339             }
10340             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10341             // and are rendered into the cells after the row is rendered - using the id for the element.
10342             
10343             if(typeof(value) === 'object'){
10344                 id = Roo.id();
10345                 cellObjects.push({
10346                     container : id,
10347                     cfg : value 
10348                 })
10349             }
10350             
10351             var rowcfg = {
10352                 record: d,
10353                 rowIndex : rowIndex,
10354                 colIndex : i,
10355                 rowClass : ''
10356             };
10357
10358             this.fireEvent('rowclass', this, rowcfg);
10359             
10360             var td = {
10361                 tag: 'td',
10362                 // this might end up displaying HTML?
10363                 // this is too messy... - better to only do it on columsn you know are going to be too long
10364                 //tooltip : (typeof(value) === 'object') ? '' : value,
10365                 cls : rowcfg.rowClass + ' x-col-' + i,
10366                 style: '',
10367                 html: (typeof(value) === 'object') ? '' : value
10368             };
10369             
10370             if (id) {
10371                 td.id = id;
10372             }
10373             
10374             if(typeof(config.colspan) != 'undefined'){
10375                 td.colspan = config.colspan;
10376             }
10377             
10378             
10379             
10380             if(typeof(config.align) != 'undefined' && config.align.length){
10381                 td.style += ' text-align:' + config.align + ';';
10382             }
10383             if(typeof(config.valign) != 'undefined' && config.valign.length){
10384                 td.style += ' vertical-align:' + config.valign + ';';
10385             }
10386             /*
10387             if(typeof(config.width) != 'undefined'){
10388                 td.style += ' width:' +  config.width + 'px;';
10389             }
10390             */
10391             
10392             if(typeof(config.cursor) != 'undefined'){
10393                 td.style += ' cursor:' +  config.cursor + ';';
10394             }
10395             
10396             if(typeof(config.cls) != 'undefined'){
10397                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10398             }
10399             if (this.responsive) {
10400                 ['xs','sm','md','lg'].map(function(size){
10401                     
10402                     if(typeof(config[size]) == 'undefined'){
10403                         return;
10404                     }
10405                     
10406                     
10407                       
10408                     if (!config[size]) { // 0 = hidden
10409                         // BS 4 '0' is treated as hide that column and below.
10410                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10411                         return;
10412                     }
10413                     
10414                     td.cls += ' col-' + size + '-' + config[size] + (
10415                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10416                     );
10417                      
10418     
10419                 });
10420             }
10421             row.cn.push(td);
10422            
10423         }
10424         
10425         row.cellObjects = cellObjects;
10426         
10427         return row;
10428           
10429     },
10430     
10431     
10432     
10433     onBeforeLoad : function()
10434     {
10435         this.el.unmask(); // if needed.
10436     },
10437      /**
10438      * Remove all rows
10439      */
10440     clear : function()
10441     {
10442         this.el.select('tbody', true).first().dom.innerHTML = '';
10443     },
10444     /**
10445      * Show or hide a row.
10446      * @param {Number} rowIndex to show or hide
10447      * @param {Boolean} state hide
10448      */
10449     setRowVisibility : function(rowIndex, state)
10450     {
10451         var bt = this.bodyEl.dom;
10452         
10453         var rows = this.el.select('tbody > tr', true).elements;
10454         
10455         if(typeof(rows[rowIndex]) == 'undefined'){
10456             return;
10457         }
10458         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10459         
10460     },
10461     
10462     
10463     getSelectionModel : function(){
10464         if(!this.selModel){
10465             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10466         }
10467         return this.selModel;
10468     },
10469     /*
10470      * Render the Roo.bootstrap object from renderder
10471      */
10472     renderCellObject : function(r)
10473     {
10474         var _this = this;
10475         
10476         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10477         
10478         var t = r.cfg.render(r.container);
10479         
10480         if(r.cfg.cn){
10481             Roo.each(r.cfg.cn, function(c){
10482                 var child = {
10483                     container: t.getChildContainer(),
10484                     cfg: c
10485                 };
10486                 _this.renderCellObject(child);
10487             })
10488         }
10489     },
10490     /**
10491      * get the Row Index from a dom element.
10492      * @param {Roo.Element} row The row to look for
10493      * @returns {Number} the row
10494      */
10495     getRowIndex : function(row)
10496     {
10497         var rowIndex = -1;
10498         
10499         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10500             if(el != row){
10501                 return;
10502             }
10503             
10504             rowIndex = index;
10505         });
10506         
10507         return rowIndex;
10508     },
10509     /**
10510      * get the header TH element for columnIndex
10511      * @param {Number} columnIndex
10512      * @returns {Roo.Element}
10513      */
10514     getHeaderIndex: function(colIndex)
10515     {
10516         var cols = this.headEl.select('th', true).elements;
10517         return cols[colIndex]; 
10518     },
10519     /**
10520      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10521      * @param {domElement} cell to look for
10522      * @returns {Number} the column
10523      */
10524     getCellIndex : function(cell)
10525     {
10526         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10527         if(id){
10528             return parseInt(id[1], 10);
10529         }
10530         return 0;
10531     },
10532      /**
10533      * Returns the grid's underlying element = used by panel.Grid
10534      * @return {Element} The element
10535      */
10536     getGridEl : function(){
10537         return this.el;
10538     },
10539      /**
10540      * Forces a resize - used by panel.Grid
10541      * @return {Element} The element
10542      */
10543     autoSize : function()
10544     {
10545         if(this.disableAutoSize) {
10546             return;
10547         }
10548         //var ctr = Roo.get(this.container.dom.parentElement);
10549         var ctr = Roo.get(this.el.dom);
10550         
10551         var thd = this.getGridEl().select('thead',true).first();
10552         var tbd = this.getGridEl().select('tbody', true).first();
10553         var tfd = this.getGridEl().select('tfoot', true).first();
10554         
10555         var cw = ctr.getWidth();
10556         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10557         
10558         if (tbd) {
10559             
10560             tbd.setWidth(ctr.getWidth());
10561             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10562             // this needs fixing for various usage - currently only hydra job advers I think..
10563             //tdb.setHeight(
10564             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10565             //); 
10566             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10567             cw -= barsize;
10568         }
10569         cw = Math.max(cw, this.totalWidth);
10570         this.getGridEl().select('tbody tr',true).setWidth(cw);
10571         this.initCSS();
10572         
10573         // resize 'expandable coloumn?
10574         
10575         return; // we doe not have a view in this design..
10576         
10577     },
10578     onBodyScroll: function()
10579     {
10580         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10581         if(this.headEl){
10582             this.headEl.setStyle({
10583                 'position' : 'relative',
10584                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10585             });
10586         }
10587         
10588         if(this.lazyLoad){
10589             
10590             var scrollHeight = this.bodyEl.dom.scrollHeight;
10591             
10592             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10593             
10594             var height = this.bodyEl.getHeight();
10595             
10596             if(scrollHeight - height == scrollTop) {
10597                 
10598                 var total = this.ds.getTotalCount();
10599                 
10600                 if(this.footer.cursor + this.footer.pageSize < total){
10601                     
10602                     this.footer.ds.load({
10603                         params : {
10604                             start : this.footer.cursor + this.footer.pageSize,
10605                             limit : this.footer.pageSize
10606                         },
10607                         add : true
10608                     });
10609                 }
10610             }
10611             
10612         }
10613     },
10614     onColumnSplitterMoved : function(i, diff)
10615     {
10616         this.userResized = true;
10617         
10618         var cm = this.colModel;
10619         
10620         var w = this.getHeaderIndex(i).getWidth() + diff;
10621         
10622         
10623         cm.setColumnWidth(i, w, true);
10624         this.initCSS();
10625         //var cid = cm.getColumnId(i); << not used in this version?
10626        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10627         
10628         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10629         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10630         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10631 */
10632         //this.updateSplitters();
10633         //this.layout(); << ??
10634         this.fireEvent("columnresize", i, w);
10635     },
10636     onHeaderChange : function()
10637     {
10638         var header = this.renderHeader();
10639         var table = this.el.select('table', true).first();
10640         
10641         this.headEl.remove();
10642         this.headEl = table.createChild(header, this.bodyEl, false);
10643         
10644         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10645             e.on('click', this.sort, this);
10646         }, this);
10647         
10648         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10649             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10650         }
10651         
10652     },
10653     
10654     onHiddenChange : function(colModel, colIndex, hidden)
10655     {
10656         /*
10657         this.cm.setHidden()
10658         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10659         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10660         
10661         this.CSS.updateRule(thSelector, "display", "");
10662         this.CSS.updateRule(tdSelector, "display", "");
10663         
10664         if(hidden){
10665             this.CSS.updateRule(thSelector, "display", "none");
10666             this.CSS.updateRule(tdSelector, "display", "none");
10667         }
10668         */
10669         // onload calls initCSS()
10670         this.onHeaderChange();
10671         this.onLoad();
10672     },
10673     
10674     setColumnWidth: function(col_index, width)
10675     {
10676         // width = "md-2 xs-2..."
10677         if(!this.colModel.config[col_index]) {
10678             return;
10679         }
10680         
10681         var w = width.split(" ");
10682         
10683         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10684         
10685         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10686         
10687         
10688         for(var j = 0; j < w.length; j++) {
10689             
10690             if(!w[j]) {
10691                 continue;
10692             }
10693             
10694             var size_cls = w[j].split("-");
10695             
10696             if(!Number.isInteger(size_cls[1] * 1)) {
10697                 continue;
10698             }
10699             
10700             if(!this.colModel.config[col_index][size_cls[0]]) {
10701                 continue;
10702             }
10703             
10704             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10705                 continue;
10706             }
10707             
10708             h_row[0].classList.replace(
10709                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10710                 "col-"+size_cls[0]+"-"+size_cls[1]
10711             );
10712             
10713             for(var i = 0; i < rows.length; i++) {
10714                 
10715                 var size_cls = w[j].split("-");
10716                 
10717                 if(!Number.isInteger(size_cls[1] * 1)) {
10718                     continue;
10719                 }
10720                 
10721                 if(!this.colModel.config[col_index][size_cls[0]]) {
10722                     continue;
10723                 }
10724                 
10725                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10726                     continue;
10727                 }
10728                 
10729                 rows[i].classList.replace(
10730                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10731                     "col-"+size_cls[0]+"-"+size_cls[1]
10732                 );
10733             }
10734             
10735             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10736         }
10737     }
10738 });
10739
10740 // currently only used to find the split on drag.. 
10741 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10742
10743 /**
10744  * @depricated
10745 */
10746 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10747 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10748 /*
10749  * - LGPL
10750  *
10751  * table cell
10752  * 
10753  */
10754
10755 /**
10756  * @class Roo.bootstrap.TableCell
10757  * @extends Roo.bootstrap.Component
10758  * @children Roo.bootstrap.Component
10759  * @parent Roo.bootstrap.TableRow
10760  * Bootstrap TableCell class
10761  * 
10762  * @cfg {String} html cell contain text
10763  * @cfg {String} cls cell class
10764  * @cfg {String} tag cell tag (td|th) default td
10765  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10766  * @cfg {String} align Aligns the content in a cell
10767  * @cfg {String} axis Categorizes cells
10768  * @cfg {String} bgcolor Specifies the background color of a cell
10769  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10770  * @cfg {Number} colspan Specifies the number of columns a cell should span
10771  * @cfg {String} headers Specifies one or more header cells a cell is related to
10772  * @cfg {Number} height Sets the height of a cell
10773  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10774  * @cfg {Number} rowspan Sets the number of rows a cell should span
10775  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10776  * @cfg {String} valign Vertical aligns the content in a cell
10777  * @cfg {Number} width Specifies the width of a cell
10778  * 
10779  * @constructor
10780  * Create a new TableCell
10781  * @param {Object} config The config object
10782  */
10783
10784 Roo.bootstrap.TableCell = function(config){
10785     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10786 };
10787
10788 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10789     
10790     html: false,
10791     cls: false,
10792     tag: false,
10793     abbr: false,
10794     align: false,
10795     axis: false,
10796     bgcolor: false,
10797     charoff: false,
10798     colspan: false,
10799     headers: false,
10800     height: false,
10801     nowrap: false,
10802     rowspan: false,
10803     scope: false,
10804     valign: false,
10805     width: false,
10806     
10807     
10808     getAutoCreate : function(){
10809         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10810         
10811         cfg = {
10812             tag: 'td'
10813         };
10814         
10815         if(this.tag){
10816             cfg.tag = this.tag;
10817         }
10818         
10819         if (this.html) {
10820             cfg.html=this.html
10821         }
10822         if (this.cls) {
10823             cfg.cls=this.cls
10824         }
10825         if (this.abbr) {
10826             cfg.abbr=this.abbr
10827         }
10828         if (this.align) {
10829             cfg.align=this.align
10830         }
10831         if (this.axis) {
10832             cfg.axis=this.axis
10833         }
10834         if (this.bgcolor) {
10835             cfg.bgcolor=this.bgcolor
10836         }
10837         if (this.charoff) {
10838             cfg.charoff=this.charoff
10839         }
10840         if (this.colspan) {
10841             cfg.colspan=this.colspan
10842         }
10843         if (this.headers) {
10844             cfg.headers=this.headers
10845         }
10846         if (this.height) {
10847             cfg.height=this.height
10848         }
10849         if (this.nowrap) {
10850             cfg.nowrap=this.nowrap
10851         }
10852         if (this.rowspan) {
10853             cfg.rowspan=this.rowspan
10854         }
10855         if (this.scope) {
10856             cfg.scope=this.scope
10857         }
10858         if (this.valign) {
10859             cfg.valign=this.valign
10860         }
10861         if (this.width) {
10862             cfg.width=this.width
10863         }
10864         
10865         
10866         return cfg;
10867     }
10868    
10869 });
10870
10871  
10872
10873  /*
10874  * - LGPL
10875  *
10876  * table row
10877  * 
10878  */
10879
10880 /**
10881  * @class Roo.bootstrap.TableRow
10882  * @extends Roo.bootstrap.Component
10883  * @children Roo.bootstrap.TableCell
10884  * @parent Roo.bootstrap.TableBody
10885  * Bootstrap TableRow class
10886  * @cfg {String} cls row class
10887  * @cfg {String} align Aligns the content in a table row
10888  * @cfg {String} bgcolor Specifies a background color for a table row
10889  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10890  * @cfg {String} valign Vertical aligns the content in a table row
10891  * 
10892  * @constructor
10893  * Create a new TableRow
10894  * @param {Object} config The config object
10895  */
10896
10897 Roo.bootstrap.TableRow = function(config){
10898     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10899 };
10900
10901 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10902     
10903     cls: false,
10904     align: false,
10905     bgcolor: false,
10906     charoff: false,
10907     valign: false,
10908     
10909     getAutoCreate : function(){
10910         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10911         
10912         cfg = {
10913             tag: 'tr'
10914         };
10915             
10916         if(this.cls){
10917             cfg.cls = this.cls;
10918         }
10919         if(this.align){
10920             cfg.align = this.align;
10921         }
10922         if(this.bgcolor){
10923             cfg.bgcolor = this.bgcolor;
10924         }
10925         if(this.charoff){
10926             cfg.charoff = this.charoff;
10927         }
10928         if(this.valign){
10929             cfg.valign = this.valign;
10930         }
10931         
10932         return cfg;
10933     }
10934    
10935 });
10936
10937  
10938
10939  /*
10940  * - LGPL
10941  *
10942  * table body
10943  * 
10944  */
10945
10946 /**
10947  * @class Roo.bootstrap.TableBody
10948  * @extends Roo.bootstrap.Component
10949  * @children Roo.bootstrap.TableRow
10950  * @parent Roo.bootstrap.Table
10951  * Bootstrap TableBody class
10952  * @cfg {String} cls element class
10953  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10954  * @cfg {String} align Aligns the content inside the element
10955  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10956  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10957  * 
10958  * @constructor
10959  * Create a new TableBody
10960  * @param {Object} config The config object
10961  */
10962
10963 Roo.bootstrap.TableBody = function(config){
10964     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10965 };
10966
10967 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10968     
10969     cls: false,
10970     tag: false,
10971     align: false,
10972     charoff: false,
10973     valign: false,
10974     
10975     getAutoCreate : function(){
10976         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10977         
10978         cfg = {
10979             tag: 'tbody'
10980         };
10981             
10982         if (this.cls) {
10983             cfg.cls=this.cls
10984         }
10985         if(this.tag){
10986             cfg.tag = this.tag;
10987         }
10988         
10989         if(this.align){
10990             cfg.align = this.align;
10991         }
10992         if(this.charoff){
10993             cfg.charoff = this.charoff;
10994         }
10995         if(this.valign){
10996             cfg.valign = this.valign;
10997         }
10998         
10999         return cfg;
11000     }
11001     
11002     
11003 //    initEvents : function()
11004 //    {
11005 //        
11006 //        if(!this.store){
11007 //            return;
11008 //        }
11009 //        
11010 //        this.store = Roo.factory(this.store, Roo.data);
11011 //        this.store.on('load', this.onLoad, this);
11012 //        
11013 //        this.store.load();
11014 //        
11015 //    },
11016 //    
11017 //    onLoad: function () 
11018 //    {   
11019 //        this.fireEvent('load', this);
11020 //    }
11021 //    
11022 //   
11023 });
11024
11025  
11026
11027  /*
11028  * Based on:
11029  * Ext JS Library 1.1.1
11030  * Copyright(c) 2006-2007, Ext JS, LLC.
11031  *
11032  * Originally Released Under LGPL - original licence link has changed is not relivant.
11033  *
11034  * Fork - LGPL
11035  * <script type="text/javascript">
11036  */
11037
11038 // as we use this in bootstrap.
11039 Roo.namespace('Roo.form');
11040  /**
11041  * @class Roo.form.Action
11042  * Internal Class used to handle form actions
11043  * @constructor
11044  * @param {Roo.form.BasicForm} el The form element or its id
11045  * @param {Object} config Configuration options
11046  */
11047
11048  
11049  
11050 // define the action interface
11051 Roo.form.Action = function(form, options){
11052     this.form = form;
11053     this.options = options || {};
11054 };
11055 /**
11056  * Client Validation Failed
11057  * @const 
11058  */
11059 Roo.form.Action.CLIENT_INVALID = 'client';
11060 /**
11061  * Server Validation Failed
11062  * @const 
11063  */
11064 Roo.form.Action.SERVER_INVALID = 'server';
11065  /**
11066  * Connect to Server Failed
11067  * @const 
11068  */
11069 Roo.form.Action.CONNECT_FAILURE = 'connect';
11070 /**
11071  * Reading Data from Server Failed
11072  * @const 
11073  */
11074 Roo.form.Action.LOAD_FAILURE = 'load';
11075
11076 Roo.form.Action.prototype = {
11077     type : 'default',
11078     failureType : undefined,
11079     response : undefined,
11080     result : undefined,
11081
11082     // interface method
11083     run : function(options){
11084
11085     },
11086
11087     // interface method
11088     success : function(response){
11089
11090     },
11091
11092     // interface method
11093     handleResponse : function(response){
11094
11095     },
11096
11097     // default connection failure
11098     failure : function(response){
11099         
11100         this.response = response;
11101         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11102         this.form.afterAction(this, false);
11103     },
11104
11105     processResponse : function(response){
11106         this.response = response;
11107         if(!response.responseText){
11108             return true;
11109         }
11110         this.result = this.handleResponse(response);
11111         return this.result;
11112     },
11113
11114     // utility functions used internally
11115     getUrl : function(appendParams){
11116         var url = this.options.url || this.form.url || this.form.el.dom.action;
11117         if(appendParams){
11118             var p = this.getParams();
11119             if(p){
11120                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11121             }
11122         }
11123         return url;
11124     },
11125
11126     getMethod : function(){
11127         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11128     },
11129
11130     getParams : function(){
11131         var bp = this.form.baseParams;
11132         var p = this.options.params;
11133         if(p){
11134             if(typeof p == "object"){
11135                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11136             }else if(typeof p == 'string' && bp){
11137                 p += '&' + Roo.urlEncode(bp);
11138             }
11139         }else if(bp){
11140             p = Roo.urlEncode(bp);
11141         }
11142         return p;
11143     },
11144
11145     createCallback : function(){
11146         return {
11147             success: this.success,
11148             failure: this.failure,
11149             scope: this,
11150             timeout: (this.form.timeout*1000),
11151             upload: this.form.fileUpload ? this.success : undefined
11152         };
11153     }
11154 };
11155
11156 Roo.form.Action.Submit = function(form, options){
11157     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11158 };
11159
11160 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11161     type : 'submit',
11162
11163     haveProgress : false,
11164     uploadComplete : false,
11165     
11166     // uploadProgress indicator.
11167     uploadProgress : function()
11168     {
11169         if (!this.form.progressUrl) {
11170             return;
11171         }
11172         
11173         if (!this.haveProgress) {
11174             Roo.MessageBox.progress("Uploading", "Uploading");
11175         }
11176         if (this.uploadComplete) {
11177            Roo.MessageBox.hide();
11178            return;
11179         }
11180         
11181         this.haveProgress = true;
11182    
11183         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11184         
11185         var c = new Roo.data.Connection();
11186         c.request({
11187             url : this.form.progressUrl,
11188             params: {
11189                 id : uid
11190             },
11191             method: 'GET',
11192             success : function(req){
11193                //console.log(data);
11194                 var rdata = false;
11195                 var edata;
11196                 try  {
11197                    rdata = Roo.decode(req.responseText)
11198                 } catch (e) {
11199                     Roo.log("Invalid data from server..");
11200                     Roo.log(edata);
11201                     return;
11202                 }
11203                 if (!rdata || !rdata.success) {
11204                     Roo.log(rdata);
11205                     Roo.MessageBox.alert(Roo.encode(rdata));
11206                     return;
11207                 }
11208                 var data = rdata.data;
11209                 
11210                 if (this.uploadComplete) {
11211                    Roo.MessageBox.hide();
11212                    return;
11213                 }
11214                    
11215                 if (data){
11216                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11217                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11218                     );
11219                 }
11220                 this.uploadProgress.defer(2000,this);
11221             },
11222        
11223             failure: function(data) {
11224                 Roo.log('progress url failed ');
11225                 Roo.log(data);
11226             },
11227             scope : this
11228         });
11229            
11230     },
11231     
11232     
11233     run : function()
11234     {
11235         // run get Values on the form, so it syncs any secondary forms.
11236         this.form.getValues();
11237         
11238         var o = this.options;
11239         var method = this.getMethod();
11240         var isPost = method == 'POST';
11241         if(o.clientValidation === false || this.form.isValid()){
11242             
11243             if (this.form.progressUrl) {
11244                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11245                     (new Date() * 1) + '' + Math.random());
11246                     
11247             } 
11248             
11249             
11250             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11251                 form:this.form.el.dom,
11252                 url:this.getUrl(!isPost),
11253                 method: method,
11254                 params:isPost ? this.getParams() : null,
11255                 isUpload: this.form.fileUpload,
11256                 formData : this.form.formData
11257             }));
11258             
11259             this.uploadProgress();
11260
11261         }else if (o.clientValidation !== false){ // client validation failed
11262             this.failureType = Roo.form.Action.CLIENT_INVALID;
11263             this.form.afterAction(this, false);
11264         }
11265     },
11266
11267     success : function(response)
11268     {
11269         this.uploadComplete= true;
11270         if (this.haveProgress) {
11271             Roo.MessageBox.hide();
11272         }
11273         
11274         
11275         var result = this.processResponse(response);
11276         if(result === true || result.success){
11277             this.form.afterAction(this, true);
11278             return;
11279         }
11280         if(result.errors){
11281             this.form.markInvalid(result.errors);
11282             this.failureType = Roo.form.Action.SERVER_INVALID;
11283         }
11284         this.form.afterAction(this, false);
11285     },
11286     failure : function(response)
11287     {
11288         this.uploadComplete= true;
11289         if (this.haveProgress) {
11290             Roo.MessageBox.hide();
11291         }
11292         
11293         this.response = response;
11294         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11295         this.form.afterAction(this, false);
11296     },
11297     
11298     handleResponse : function(response){
11299         if(this.form.errorReader){
11300             var rs = this.form.errorReader.read(response);
11301             var errors = [];
11302             if(rs.records){
11303                 for(var i = 0, len = rs.records.length; i < len; i++) {
11304                     var r = rs.records[i];
11305                     errors[i] = r.data;
11306                 }
11307             }
11308             if(errors.length < 1){
11309                 errors = null;
11310             }
11311             return {
11312                 success : rs.success,
11313                 errors : errors
11314             };
11315         }
11316         var ret = false;
11317         try {
11318             var rt = response.responseText;
11319             if (rt.match(/^\<!--\[CDATA\[/)) {
11320                 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11321                 rt = rt.replace(/\]\]--\>$/,'');
11322             }
11323             
11324             ret = Roo.decode(rt);
11325         } catch (e) {
11326             ret = {
11327                 success: false,
11328                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11329                 errors : []
11330             };
11331         }
11332         return ret;
11333         
11334     }
11335 });
11336
11337
11338 Roo.form.Action.Load = function(form, options){
11339     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11340     this.reader = this.form.reader;
11341 };
11342
11343 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11344     type : 'load',
11345
11346     run : function(){
11347         
11348         Roo.Ajax.request(Roo.apply(
11349                 this.createCallback(), {
11350                     method:this.getMethod(),
11351                     url:this.getUrl(false),
11352                     params:this.getParams()
11353         }));
11354     },
11355
11356     success : function(response){
11357         
11358         var result = this.processResponse(response);
11359         if(result === true || !result.success || !result.data){
11360             this.failureType = Roo.form.Action.LOAD_FAILURE;
11361             this.form.afterAction(this, false);
11362             return;
11363         }
11364         this.form.clearInvalid();
11365         this.form.setValues(result.data);
11366         this.form.afterAction(this, true);
11367     },
11368
11369     handleResponse : function(response){
11370         if(this.form.reader){
11371             var rs = this.form.reader.read(response);
11372             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11373             return {
11374                 success : rs.success,
11375                 data : data
11376             };
11377         }
11378         return Roo.decode(response.responseText);
11379     }
11380 });
11381
11382 Roo.form.Action.ACTION_TYPES = {
11383     'load' : Roo.form.Action.Load,
11384     'submit' : Roo.form.Action.Submit
11385 };/*
11386  * - LGPL
11387  *
11388  * form
11389  *
11390  */
11391
11392 /**
11393  * @class Roo.bootstrap.form.Form
11394  * @extends Roo.bootstrap.Component
11395  * @children Roo.bootstrap.Component
11396  * Bootstrap Form class
11397  * @cfg {String} method  GET | POST (default POST)
11398  * @cfg {String} labelAlign top | left (default top)
11399  * @cfg {String} align left  | right - for navbars
11400  * @cfg {Boolean} loadMask load mask when submit (default true)
11401
11402  *
11403  * @constructor
11404  * Create a new Form
11405  * @param {Object} config The config object
11406  */
11407
11408
11409 Roo.bootstrap.form.Form = function(config){
11410     
11411     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11412     
11413     Roo.bootstrap.form.Form.popover.apply();
11414     
11415     this.addEvents({
11416         /**
11417          * @event clientvalidation
11418          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11419          * @param {Form} this
11420          * @param {Boolean} valid true if the form has passed client-side validation
11421          */
11422         clientvalidation: true,
11423         /**
11424          * @event beforeaction
11425          * Fires before any action is performed. Return false to cancel the action.
11426          * @param {Form} this
11427          * @param {Action} action The action to be performed
11428          */
11429         beforeaction: true,
11430         /**
11431          * @event actionfailed
11432          * Fires when an action fails.
11433          * @param {Form} this
11434          * @param {Action} action The action that failed
11435          */
11436         actionfailed : true,
11437         /**
11438          * @event actioncomplete
11439          * Fires when an action is completed.
11440          * @param {Form} this
11441          * @param {Action} action The action that completed
11442          */
11443         actioncomplete : true
11444     });
11445 };
11446
11447 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11448
11449      /**
11450      * @cfg {String} method
11451      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11452      */
11453     method : 'POST',
11454     /**
11455      * @cfg {String} url
11456      * The URL to use for form actions if one isn't supplied in the action options.
11457      */
11458     /**
11459      * @cfg {Boolean} fileUpload
11460      * Set to true if this form is a file upload.
11461      */
11462
11463     /**
11464      * @cfg {Object} baseParams
11465      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11466      */
11467
11468     /**
11469      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11470      */
11471     timeout: 30,
11472     /**
11473      * @cfg {Sting} align (left|right) for navbar forms
11474      */
11475     align : 'left',
11476
11477     // private
11478     activeAction : null,
11479
11480     /**
11481      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11482      * element by passing it or its id or mask the form itself by passing in true.
11483      * @type Mixed
11484      */
11485     waitMsgTarget : false,
11486
11487     loadMask : true,
11488     
11489     /**
11490      * @cfg {Boolean} errorMask (true|false) default false
11491      */
11492     errorMask : false,
11493     
11494     /**
11495      * @cfg {Number} maskOffset Default 100
11496      */
11497     maskOffset : 100,
11498     
11499     /**
11500      * @cfg {Boolean} maskBody
11501      */
11502     maskBody : false,
11503
11504     getAutoCreate : function(){
11505
11506         var cfg = {
11507             tag: 'form',
11508             method : this.method || 'POST',
11509             id : this.id || Roo.id(),
11510             cls : ''
11511         };
11512         if (this.parent().xtype.match(/^Nav/)) {
11513             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11514
11515         }
11516
11517         if (this.labelAlign == 'left' ) {
11518             cfg.cls += ' form-horizontal';
11519         }
11520
11521
11522         return cfg;
11523     },
11524     initEvents : function()
11525     {
11526         this.el.on('submit', this.onSubmit, this);
11527         // this was added as random key presses on the form where triggering form submit.
11528         this.el.on('keypress', function(e) {
11529             if (e.getCharCode() != 13) {
11530                 return true;
11531             }
11532             // we might need to allow it for textareas.. and some other items.
11533             // check e.getTarget().
11534
11535             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11536                 return true;
11537             }
11538
11539             Roo.log("keypress blocked");
11540
11541             e.preventDefault();
11542             return false;
11543         });
11544         
11545     },
11546     // private
11547     onSubmit : function(e){
11548         e.stopEvent();
11549     },
11550
11551      /**
11552      * Returns true if client-side validation on the form is successful.
11553      * @return Boolean
11554      */
11555     isValid : function(){
11556         var items = this.getItems();
11557         var valid = true;
11558         var target = false;
11559         
11560         items.each(function(f){
11561             
11562             if(f.validate()){
11563                 return;
11564             }
11565             
11566             Roo.log('invalid field: ' + f.name);
11567             
11568             valid = false;
11569
11570             if(!target && f.el.isVisible(true)){
11571                 target = f;
11572             }
11573            
11574         });
11575         
11576         if(this.errorMask && !valid){
11577             Roo.bootstrap.form.Form.popover.mask(this, target);
11578         }
11579         
11580         return valid;
11581     },
11582     
11583     /**
11584      * Returns true if any fields in this form have changed since their original load.
11585      * @return Boolean
11586      */
11587     isDirty : function(){
11588         var dirty = false;
11589         var items = this.getItems();
11590         items.each(function(f){
11591            if(f.isDirty()){
11592                dirty = true;
11593                return false;
11594            }
11595            return true;
11596         });
11597         return dirty;
11598     },
11599      /**
11600      * Performs a predefined action (submit or load) or custom actions you define on this form.
11601      * @param {String} actionName The name of the action type
11602      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11603      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11604      * accept other config options):
11605      * <pre>
11606 Property          Type             Description
11607 ----------------  ---------------  ----------------------------------------------------------------------------------
11608 url               String           The url for the action (defaults to the form's url)
11609 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11610 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11611 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11612                                    validate the form on the client (defaults to false)
11613      * </pre>
11614      * @return {BasicForm} this
11615      */
11616     doAction : function(action, options){
11617         if(typeof action == 'string'){
11618             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11619         }
11620         if(this.fireEvent('beforeaction', this, action) !== false){
11621             this.beforeAction(action);
11622             action.run.defer(100, action);
11623         }
11624         return this;
11625     },
11626
11627     // private
11628     beforeAction : function(action){
11629         var o = action.options;
11630         
11631         if(this.loadMask){
11632             
11633             if(this.maskBody){
11634                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11635             } else {
11636                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11637             }
11638         }
11639         // not really supported yet.. ??
11640
11641         //if(this.waitMsgTarget === true){
11642         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11643         //}else if(this.waitMsgTarget){
11644         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11645         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11646         //}else {
11647         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11648        // }
11649
11650     },
11651
11652     // private
11653     afterAction : function(action, success){
11654         this.activeAction = null;
11655         var o = action.options;
11656
11657         if(this.loadMask){
11658             
11659             if(this.maskBody){
11660                 Roo.get(document.body).unmask();
11661             } else {
11662                 this.el.unmask();
11663             }
11664         }
11665         
11666         //if(this.waitMsgTarget === true){
11667 //            this.el.unmask();
11668         //}else if(this.waitMsgTarget){
11669         //    this.waitMsgTarget.unmask();
11670         //}else{
11671         //    Roo.MessageBox.updateProgress(1);
11672         //    Roo.MessageBox.hide();
11673        // }
11674         //
11675         if(success){
11676             if(o.reset){
11677                 this.reset();
11678             }
11679             Roo.callback(o.success, o.scope, [this, action]);
11680             this.fireEvent('actioncomplete', this, action);
11681
11682         }else{
11683
11684             // failure condition..
11685             // we have a scenario where updates need confirming.
11686             // eg. if a locking scenario exists..
11687             // we look for { errors : { needs_confirm : true }} in the response.
11688             if (
11689                 (typeof(action.result) != 'undefined')  &&
11690                 (typeof(action.result.errors) != 'undefined')  &&
11691                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11692            ){
11693                 var _t = this;
11694                 Roo.log("not supported yet");
11695                  /*
11696
11697                 Roo.MessageBox.confirm(
11698                     "Change requires confirmation",
11699                     action.result.errorMsg,
11700                     function(r) {
11701                         if (r != 'yes') {
11702                             return;
11703                         }
11704                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11705                     }
11706
11707                 );
11708                 */
11709
11710
11711                 return;
11712             }
11713
11714             Roo.callback(o.failure, o.scope, [this, action]);
11715             // show an error message if no failed handler is set..
11716             if (!this.hasListener('actionfailed')) {
11717                 Roo.log("need to add dialog support");
11718                 /*
11719                 Roo.MessageBox.alert("Error",
11720                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11721                         action.result.errorMsg :
11722                         "Saving Failed, please check your entries or try again"
11723                 );
11724                 */
11725             }
11726
11727             this.fireEvent('actionfailed', this, action);
11728         }
11729
11730     },
11731     /**
11732      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11733      * @param {String} id The value to search for
11734      * @return Field
11735      */
11736     findField : function(id){
11737         var items = this.getItems();
11738         var field = items.get(id);
11739         if(!field){
11740              items.each(function(f){
11741                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11742                     field = f;
11743                     return false;
11744                 }
11745                 return true;
11746             });
11747         }
11748         return field || null;
11749     },
11750      /**
11751      * Mark fields in this form invalid in bulk.
11752      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11753      * @return {BasicForm} this
11754      */
11755     markInvalid : function(errors){
11756         if(errors instanceof Array){
11757             for(var i = 0, len = errors.length; i < len; i++){
11758                 var fieldError = errors[i];
11759                 var f = this.findField(fieldError.id);
11760                 if(f){
11761                     f.markInvalid(fieldError.msg);
11762                 }
11763             }
11764         }else{
11765             var field, id;
11766             for(id in errors){
11767                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11768                     field.markInvalid(errors[id]);
11769                 }
11770             }
11771         }
11772         //Roo.each(this.childForms || [], function (f) {
11773         //    f.markInvalid(errors);
11774         //});
11775
11776         return this;
11777     },
11778
11779     /**
11780      * Set values for fields in this form in bulk.
11781      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11782      * @return {BasicForm} this
11783      */
11784     setValues : function(values){
11785         if(values instanceof Array){ // array of objects
11786             for(var i = 0, len = values.length; i < len; i++){
11787                 var v = values[i];
11788                 var f = this.findField(v.id);
11789                 if(f){
11790                     f.setValue(v.value);
11791                     if(this.trackResetOnLoad){
11792                         f.originalValue = f.getValue();
11793                     }
11794                 }
11795             }
11796         }else{ // object hash
11797             var field, id;
11798             for(id in values){
11799                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11800
11801                     if (field.setFromData &&
11802                         field.valueField &&
11803                         field.displayField &&
11804                         // combos' with local stores can
11805                         // be queried via setValue()
11806                         // to set their value..
11807                         (field.store && !field.store.isLocal)
11808                         ) {
11809                         // it's a combo
11810                         var sd = { };
11811                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11812                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11813                         field.setFromData(sd);
11814
11815                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11816                         
11817                         field.setFromData(values);
11818                         
11819                     } else {
11820                         field.setValue(values[id]);
11821                     }
11822
11823
11824                     if(this.trackResetOnLoad){
11825                         field.originalValue = field.getValue();
11826                     }
11827                 }
11828             }
11829         }
11830
11831         //Roo.each(this.childForms || [], function (f) {
11832         //    f.setValues(values);
11833         //});
11834
11835         return this;
11836     },
11837
11838     /**
11839      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11840      * they are returned as an array.
11841      * @param {Boolean} asString
11842      * @return {Object}
11843      */
11844     getValues : function(asString){
11845         //if (this.childForms) {
11846             // copy values from the child forms
11847         //    Roo.each(this.childForms, function (f) {
11848         //        this.setValues(f.getValues());
11849         //    }, this);
11850         //}
11851
11852
11853
11854         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11855         if(asString === true){
11856             return fs;
11857         }
11858         return Roo.urlDecode(fs);
11859     },
11860
11861     /**
11862      * Returns the fields in this form as an object with key/value pairs.
11863      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11864      * @return {Object}
11865      */
11866     getFieldValues : function(with_hidden)
11867     {
11868         var items = this.getItems();
11869         var ret = {};
11870         items.each(function(f){
11871             
11872             if (!f.getName()) {
11873                 return;
11874             }
11875             
11876             var v = f.getValue();
11877             
11878             if (f.inputType =='radio') {
11879                 if (typeof(ret[f.getName()]) == 'undefined') {
11880                     ret[f.getName()] = ''; // empty..
11881                 }
11882
11883                 if (!f.el.dom.checked) {
11884                     return;
11885
11886                 }
11887                 v = f.el.dom.value;
11888
11889             }
11890             
11891             if(f.xtype == 'MoneyField'){
11892                 ret[f.currencyName] = f.getCurrency();
11893             }
11894
11895             // not sure if this supported any more..
11896             if ((typeof(v) == 'object') && f.getRawValue) {
11897                 v = f.getRawValue() ; // dates..
11898             }
11899             // combo boxes where name != hiddenName...
11900             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11901                 ret[f.name] = f.getRawValue();
11902             }
11903             ret[f.getName()] = v;
11904         });
11905
11906         return ret;
11907     },
11908
11909     /**
11910      * Clears all invalid messages in this form.
11911      * @return {BasicForm} this
11912      */
11913     clearInvalid : function(){
11914         var items = this.getItems();
11915
11916         items.each(function(f){
11917            f.clearInvalid();
11918         });
11919
11920         return this;
11921     },
11922
11923     /**
11924      * Resets this form.
11925      * @return {BasicForm} this
11926      */
11927     reset : function(){
11928         var items = this.getItems();
11929         items.each(function(f){
11930             f.reset();
11931         });
11932
11933         Roo.each(this.childForms || [], function (f) {
11934             f.reset();
11935         });
11936
11937
11938         return this;
11939     },
11940     
11941     getItems : function()
11942     {
11943         var r=new Roo.util.MixedCollection(false, function(o){
11944             return o.id || (o.id = Roo.id());
11945         });
11946         var iter = function(el) {
11947             if (el.inputEl) {
11948                 r.add(el);
11949             }
11950             if (!el.items) {
11951                 return;
11952             }
11953             Roo.each(el.items,function(e) {
11954                 iter(e);
11955             });
11956         };
11957
11958         iter(this);
11959         return r;
11960     },
11961     
11962     hideFields : function(items)
11963     {
11964         Roo.each(items, function(i){
11965             
11966             var f = this.findField(i);
11967             
11968             if(!f){
11969                 return;
11970             }
11971             
11972             f.hide();
11973             
11974         }, this);
11975     },
11976     
11977     showFields : function(items)
11978     {
11979         Roo.each(items, function(i){
11980             
11981             var f = this.findField(i);
11982             
11983             if(!f){
11984                 return;
11985             }
11986             
11987             f.show();
11988             
11989         }, this);
11990     }
11991
11992 });
11993
11994 Roo.apply(Roo.bootstrap.form.Form, {
11995     
11996     popover : {
11997         
11998         padding : 5,
11999         
12000         isApplied : false,
12001         
12002         isMasked : false,
12003         
12004         form : false,
12005         
12006         target : false,
12007         
12008         toolTip : false,
12009         
12010         intervalID : false,
12011         
12012         maskEl : false,
12013         
12014         apply : function()
12015         {
12016             if(this.isApplied){
12017                 return;
12018             }
12019             
12020             this.maskEl = {
12021                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12022                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12023                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12024                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12025             };
12026             
12027             this.maskEl.top.enableDisplayMode("block");
12028             this.maskEl.left.enableDisplayMode("block");
12029             this.maskEl.bottom.enableDisplayMode("block");
12030             this.maskEl.right.enableDisplayMode("block");
12031             
12032             this.toolTip = new Roo.bootstrap.Tooltip({
12033                 cls : 'roo-form-error-popover',
12034                 alignment : {
12035                     'left' : ['r-l', [-2,0], 'right'],
12036                     'right' : ['l-r', [2,0], 'left'],
12037                     'bottom' : ['tl-bl', [0,2], 'top'],
12038                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12039                 }
12040             });
12041             
12042             this.toolTip.render(Roo.get(document.body));
12043
12044             this.toolTip.el.enableDisplayMode("block");
12045             
12046             Roo.get(document.body).on('click', function(){
12047                 this.unmask();
12048             }, this);
12049             
12050             Roo.get(document.body).on('touchstart', function(){
12051                 this.unmask();
12052             }, this);
12053             
12054             this.isApplied = true
12055         },
12056         
12057         mask : function(form, target)
12058         {
12059             this.form = form;
12060             
12061             this.target = target;
12062             
12063             if(!this.form.errorMask || !target.el){
12064                 return;
12065             }
12066             
12067             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12068             
12069             Roo.log(scrollable);
12070             
12071             var ot = this.target.el.calcOffsetsTo(scrollable);
12072             
12073             var scrollTo = ot[1] - this.form.maskOffset;
12074             
12075             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12076             
12077             scrollable.scrollTo('top', scrollTo);
12078             
12079             var box = this.target.el.getBox();
12080             Roo.log(box);
12081             var zIndex = Roo.bootstrap.Modal.zIndex++;
12082
12083             
12084             this.maskEl.top.setStyle('position', 'absolute');
12085             this.maskEl.top.setStyle('z-index', zIndex);
12086             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12087             this.maskEl.top.setLeft(0);
12088             this.maskEl.top.setTop(0);
12089             this.maskEl.top.show();
12090             
12091             this.maskEl.left.setStyle('position', 'absolute');
12092             this.maskEl.left.setStyle('z-index', zIndex);
12093             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12094             this.maskEl.left.setLeft(0);
12095             this.maskEl.left.setTop(box.y - this.padding);
12096             this.maskEl.left.show();
12097
12098             this.maskEl.bottom.setStyle('position', 'absolute');
12099             this.maskEl.bottom.setStyle('z-index', zIndex);
12100             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12101             this.maskEl.bottom.setLeft(0);
12102             this.maskEl.bottom.setTop(box.bottom + this.padding);
12103             this.maskEl.bottom.show();
12104
12105             this.maskEl.right.setStyle('position', 'absolute');
12106             this.maskEl.right.setStyle('z-index', zIndex);
12107             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12108             this.maskEl.right.setLeft(box.right + this.padding);
12109             this.maskEl.right.setTop(box.y - this.padding);
12110             this.maskEl.right.show();
12111
12112             this.toolTip.bindEl = this.target.el;
12113
12114             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12115
12116             var tip = this.target.blankText;
12117
12118             if(this.target.getValue() !== '' ) {
12119                 
12120                 if (this.target.invalidText.length) {
12121                     tip = this.target.invalidText;
12122                 } else if (this.target.regexText.length){
12123                     tip = this.target.regexText;
12124                 }
12125             }
12126
12127             this.toolTip.show(tip);
12128
12129             this.intervalID = window.setInterval(function() {
12130                 Roo.bootstrap.form.Form.popover.unmask();
12131             }, 10000);
12132
12133             window.onwheel = function(){ return false;};
12134             
12135             (function(){ this.isMasked = true; }).defer(500, this);
12136             
12137         },
12138         
12139         unmask : function()
12140         {
12141             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12142                 return;
12143             }
12144             
12145             this.maskEl.top.setStyle('position', 'absolute');
12146             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12147             this.maskEl.top.hide();
12148
12149             this.maskEl.left.setStyle('position', 'absolute');
12150             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12151             this.maskEl.left.hide();
12152
12153             this.maskEl.bottom.setStyle('position', 'absolute');
12154             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12155             this.maskEl.bottom.hide();
12156
12157             this.maskEl.right.setStyle('position', 'absolute');
12158             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12159             this.maskEl.right.hide();
12160             
12161             this.toolTip.hide();
12162             
12163             this.toolTip.el.hide();
12164             
12165             window.onwheel = function(){ return true;};
12166             
12167             if(this.intervalID){
12168                 window.clearInterval(this.intervalID);
12169                 this.intervalID = false;
12170             }
12171             
12172             this.isMasked = false;
12173             
12174         }
12175         
12176     }
12177     
12178 });
12179
12180 /*
12181  * Based on:
12182  * Ext JS Library 1.1.1
12183  * Copyright(c) 2006-2007, Ext JS, LLC.
12184  *
12185  * Originally Released Under LGPL - original licence link has changed is not relivant.
12186  *
12187  * Fork - LGPL
12188  * <script type="text/javascript">
12189  */
12190 /**
12191  * @class Roo.form.VTypes
12192  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12193  * @static
12194  */
12195 Roo.form.VTypes = function(){
12196     // closure these in so they are only created once.
12197     var alpha = /^[a-zA-Z_]+$/;
12198     var alphanum = /^[a-zA-Z0-9_]+$/;
12199     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12200     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12201
12202     // All these messages and functions are configurable
12203     return {
12204         /**
12205          * The function used to validate email addresses
12206          * @param {String} value The email address
12207          */
12208         email : function(v){
12209             return email.test(v);
12210         },
12211         /**
12212          * The error text to display when the email validation function returns false
12213          * @type String
12214          */
12215         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12216         /**
12217          * The keystroke filter mask to be applied on email input
12218          * @type RegExp
12219          */
12220         emailMask : /[a-z0-9_\.\-@]/i,
12221
12222         /**
12223          * The function used to validate URLs
12224          * @param {String} value The URL
12225          */
12226         url : function(v){
12227             return url.test(v);
12228         },
12229         /**
12230          * The error text to display when the url validation function returns false
12231          * @type String
12232          */
12233         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12234         
12235         /**
12236          * The function used to validate alpha values
12237          * @param {String} value The value
12238          */
12239         alpha : function(v){
12240             return alpha.test(v);
12241         },
12242         /**
12243          * The error text to display when the alpha validation function returns false
12244          * @type String
12245          */
12246         alphaText : 'This field should only contain letters and _',
12247         /**
12248          * The keystroke filter mask to be applied on alpha input
12249          * @type RegExp
12250          */
12251         alphaMask : /[a-z_]/i,
12252
12253         /**
12254          * The function used to validate alphanumeric values
12255          * @param {String} value The value
12256          */
12257         alphanum : function(v){
12258             return alphanum.test(v);
12259         },
12260         /**
12261          * The error text to display when the alphanumeric validation function returns false
12262          * @type String
12263          */
12264         alphanumText : 'This field should only contain letters, numbers and _',
12265         /**
12266          * The keystroke filter mask to be applied on alphanumeric input
12267          * @type RegExp
12268          */
12269         alphanumMask : /[a-z0-9_]/i
12270     };
12271 }();/*
12272  * - LGPL
12273  *
12274  * Input
12275  * 
12276  */
12277
12278 /**
12279  * @class Roo.bootstrap.form.Input
12280  * @extends Roo.bootstrap.Component
12281  * Bootstrap Input class
12282  * @cfg {Boolean} disabled is it disabled
12283  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12284  * @cfg {String} name name of the input
12285  * @cfg {string} fieldLabel - the label associated
12286  * @cfg {string} placeholder - placeholder to put in text.
12287  * @cfg {string} before - input group add on before
12288  * @cfg {string} after - input group add on after
12289  * @cfg {string} size - (lg|sm) or leave empty..
12290  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12291  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12292  * @cfg {Number} md colspan out of 12 for computer-sized screens
12293  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12294  * @cfg {string} value default value of the input
12295  * @cfg {Number} labelWidth set the width of label 
12296  * @cfg {Number} labellg set the width of label (1-12)
12297  * @cfg {Number} labelmd set the width of label (1-12)
12298  * @cfg {Number} labelsm set the width of label (1-12)
12299  * @cfg {Number} labelxs set the width of label (1-12)
12300  * @cfg {String} labelAlign (top|left)
12301  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12302  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12303  * @cfg {String} indicatorpos (left|right) default left
12304  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12305  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12306  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12307  * @cfg {Roo.bootstrap.Button} before Button to show before
12308  * @cfg {Roo.bootstrap.Button} afterButton to show before
12309  * @cfg {String} align (left|center|right) Default left
12310  * @cfg {Boolean} forceFeedback (true|false) Default false
12311  * 
12312  * @constructor
12313  * Create a new Input
12314  * @param {Object} config The config object
12315  */
12316
12317 Roo.bootstrap.form.Input = function(config){
12318     
12319     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12320     
12321     this.addEvents({
12322         /**
12323          * @event focus
12324          * Fires when this field receives input focus.
12325          * @param {Roo.form.Field} this
12326          */
12327         focus : true,
12328         /**
12329          * @event blur
12330          * Fires when this field loses input focus.
12331          * @param {Roo.form.Field} this
12332          */
12333         blur : true,
12334         /**
12335          * @event specialkey
12336          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12337          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12338          * @param {Roo.form.Field} this
12339          * @param {Roo.EventObject} e The event object
12340          */
12341         specialkey : true,
12342         /**
12343          * @event change
12344          * Fires just before the field blurs if the field value has changed.
12345          * @param {Roo.form.Field} this
12346          * @param {Mixed} newValue The new value
12347          * @param {Mixed} oldValue The original value
12348          */
12349         change : true,
12350         /**
12351          * @event invalid
12352          * Fires after the field has been marked as invalid.
12353          * @param {Roo.form.Field} this
12354          * @param {String} msg The validation message
12355          */
12356         invalid : true,
12357         /**
12358          * @event valid
12359          * Fires after the field has been validated with no errors.
12360          * @param {Roo.form.Field} this
12361          */
12362         valid : true,
12363          /**
12364          * @event keyup
12365          * Fires after the key up
12366          * @param {Roo.form.Field} this
12367          * @param {Roo.EventObject}  e The event Object
12368          */
12369         keyup : true,
12370         /**
12371          * @event paste
12372          * Fires after the user pastes into input
12373          * @param {Roo.form.Field} this
12374          * @param {Roo.EventObject}  e The event Object
12375          */
12376         paste : true
12377     });
12378 };
12379
12380 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12381      /**
12382      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12383       automatic validation (defaults to "keyup").
12384      */
12385     validationEvent : "keyup",
12386      /**
12387      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12388      */
12389     validateOnBlur : true,
12390     /**
12391      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12392      */
12393     validationDelay : 250,
12394      /**
12395      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12396      */
12397     focusClass : "x-form-focus",  // not needed???
12398     
12399        
12400     /**
12401      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12402      */
12403     invalidClass : "has-warning",
12404     
12405     /**
12406      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12407      */
12408     validClass : "has-success",
12409     
12410     /**
12411      * @cfg {Boolean} hasFeedback (true|false) default true
12412      */
12413     hasFeedback : true,
12414     
12415     /**
12416      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12417      */
12418     invalidFeedbackClass : "glyphicon-warning-sign",
12419     
12420     /**
12421      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12422      */
12423     validFeedbackClass : "glyphicon-ok",
12424     
12425     /**
12426      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12427      */
12428     selectOnFocus : false,
12429     
12430      /**
12431      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12432      */
12433     maskRe : null,
12434        /**
12435      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12436      */
12437     vtype : null,
12438     
12439       /**
12440      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12441      */
12442     disableKeyFilter : false,
12443     
12444        /**
12445      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12446      */
12447     disabled : false,
12448      /**
12449      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12450      */
12451     allowBlank : true,
12452     /**
12453      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12454      */
12455     blankText : "Please complete this mandatory field",
12456     
12457      /**
12458      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12459      */
12460     minLength : 0,
12461     /**
12462      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12463      */
12464     maxLength : Number.MAX_VALUE,
12465     /**
12466      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12467      */
12468     minLengthText : "The minimum length for this field is {0}",
12469     /**
12470      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12471      */
12472     maxLengthText : "The maximum length for this field is {0}",
12473   
12474     
12475     /**
12476      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12477      * If available, this function will be called only after the basic validators all return true, and will be passed the
12478      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12479      */
12480     validator : null,
12481     /**
12482      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12483      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12484      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12485      */
12486     regex : null,
12487     /**
12488      * @cfg {String} regexText -- Depricated - use Invalid Text
12489      */
12490     regexText : "",
12491     
12492     /**
12493      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12494      */
12495     invalidText : "",
12496     
12497     
12498     
12499     autocomplete: false,
12500     
12501     
12502     fieldLabel : '',
12503     inputType : 'text',
12504     
12505     name : false,
12506     placeholder: false,
12507     before : false,
12508     after : false,
12509     size : false,
12510     hasFocus : false,
12511     preventMark: false,
12512     isFormField : true,
12513     value : '',
12514     labelWidth : 2,
12515     labelAlign : false,
12516     readOnly : false,
12517     align : false,
12518     formatedValue : false,
12519     forceFeedback : false,
12520     
12521     indicatorpos : 'left',
12522     
12523     labellg : 0,
12524     labelmd : 0,
12525     labelsm : 0,
12526     labelxs : 0,
12527     
12528     capture : '',
12529     accept : '',
12530     
12531     parentLabelAlign : function()
12532     {
12533         var parent = this;
12534         while (parent.parent()) {
12535             parent = parent.parent();
12536             if (typeof(parent.labelAlign) !='undefined') {
12537                 return parent.labelAlign;
12538             }
12539         }
12540         return 'left';
12541         
12542     },
12543     
12544     getAutoCreate : function()
12545     {
12546         
12547         var id = Roo.id();
12548         
12549         var cfg = {};
12550         
12551         if(this.inputType != 'hidden'){
12552             cfg.cls = 'form-group' //input-group
12553         }
12554         
12555         var input =  {
12556             tag: 'input',
12557             id : id,
12558             type : this.inputType,
12559             value : this.value,
12560             cls : 'form-control',
12561             placeholder : this.placeholder || '',
12562             autocomplete : this.autocomplete || 'new-password'
12563         };
12564         if (this.inputType == 'file') {
12565             input.style = 'overflow:hidden'; // why not in CSS?
12566         }
12567         
12568         if(this.capture.length){
12569             input.capture = this.capture;
12570         }
12571         
12572         if(this.accept.length){
12573             input.accept = this.accept + "/*";
12574         }
12575         
12576         if(this.align){
12577             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12578         }
12579         
12580         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12581             input.maxLength = this.maxLength;
12582         }
12583         
12584         if (this.disabled) {
12585             input.disabled=true;
12586         }
12587         
12588         if (this.readOnly) {
12589             input.readonly=true;
12590         }
12591         
12592         if (this.name) {
12593             input.name = this.name;
12594         }
12595         
12596         if (this.size) {
12597             input.cls += ' input-' + this.size;
12598         }
12599         
12600         var settings=this;
12601         ['xs','sm','md','lg'].map(function(size){
12602             if (settings[size]) {
12603                 cfg.cls += ' col-' + size + '-' + settings[size];
12604             }
12605         });
12606         
12607         var inputblock = input;
12608         
12609         var feedback = {
12610             tag: 'span',
12611             cls: 'glyphicon form-control-feedback'
12612         };
12613             
12614         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12615             
12616             inputblock = {
12617                 cls : 'has-feedback',
12618                 cn :  [
12619                     input,
12620                     feedback
12621                 ] 
12622             };  
12623         }
12624         
12625         if (this.before || this.after) {
12626             
12627             inputblock = {
12628                 cls : 'input-group',
12629                 cn :  [] 
12630             };
12631             
12632             if (this.before && typeof(this.before) == 'string') {
12633                 
12634                 inputblock.cn.push({
12635                     tag :'span',
12636                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12637                     html : this.before
12638                 });
12639             }
12640             if (this.before && typeof(this.before) == 'object') {
12641                 this.before = Roo.factory(this.before);
12642                 
12643                 inputblock.cn.push({
12644                     tag :'span',
12645                     cls : 'roo-input-before input-group-prepend   input-group-' +
12646                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12647                 });
12648             }
12649             
12650             inputblock.cn.push(input);
12651             
12652             if (this.after && typeof(this.after) == 'string') {
12653                 inputblock.cn.push({
12654                     tag :'span',
12655                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12656                     html : this.after
12657                 });
12658             }
12659             if (this.after && typeof(this.after) == 'object') {
12660                 this.after = Roo.factory(this.after);
12661                 
12662                 inputblock.cn.push({
12663                     tag :'span',
12664                     cls : 'roo-input-after input-group-append  input-group-' +
12665                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12666                 });
12667             }
12668             
12669             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12670                 inputblock.cls += ' has-feedback';
12671                 inputblock.cn.push(feedback);
12672             }
12673         };
12674         
12675         
12676         
12677         cfg = this.getAutoCreateLabel( cfg, inputblock );
12678         
12679        
12680          
12681         
12682         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12683            cfg.cls += ' navbar-form';
12684         }
12685         
12686         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12687             // on BS4 we do this only if not form 
12688             cfg.cls += ' navbar-form';
12689             cfg.tag = 'li';
12690         }
12691         
12692         return cfg;
12693         
12694     },
12695     /**
12696      * autocreate the label - also used by textara... ?? and others?
12697      */
12698     getAutoCreateLabel : function( cfg, inputblock )
12699     {
12700         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12701        
12702         var indicator = {
12703             tag : 'i',
12704             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12705             tooltip : 'This field is required'
12706         };
12707         if (this.allowBlank ) {
12708             indicator.style = this.allowBlank ? ' display:none' : '';
12709         }
12710         if (align ==='left' && this.fieldLabel.length) {
12711             
12712             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12713             
12714             cfg.cn = [
12715                 indicator,
12716                 {
12717                     tag: 'label',
12718                     'for' :  id,
12719                     cls : 'control-label col-form-label',
12720                     html : this.fieldLabel
12721
12722                 },
12723                 {
12724                     cls : "", 
12725                     cn: [
12726                         inputblock
12727                     ]
12728                 }
12729             ];
12730             
12731             var labelCfg = cfg.cn[1];
12732             var contentCfg = cfg.cn[2];
12733             
12734             if(this.indicatorpos == 'right'){
12735                 cfg.cn = [
12736                     {
12737                         tag: 'label',
12738                         'for' :  id,
12739                         cls : 'control-label col-form-label',
12740                         cn : [
12741                             {
12742                                 tag : 'span',
12743                                 html : this.fieldLabel
12744                             },
12745                             indicator
12746                         ]
12747                     },
12748                     {
12749                         cls : "",
12750                         cn: [
12751                             inputblock
12752                         ]
12753                     }
12754
12755                 ];
12756                 
12757                 labelCfg = cfg.cn[0];
12758                 contentCfg = cfg.cn[1];
12759             
12760             }
12761             
12762             if(this.labelWidth > 12){
12763                 labelCfg.style = "width: " + this.labelWidth + 'px';
12764             }
12765             
12766             if(this.labelWidth < 13 && this.labelmd == 0){
12767                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12768             }
12769             
12770             if(this.labellg > 0){
12771                 labelCfg.cls += ' col-lg-' + this.labellg;
12772                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12773             }
12774             
12775             if(this.labelmd > 0){
12776                 labelCfg.cls += ' col-md-' + this.labelmd;
12777                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12778             }
12779             
12780             if(this.labelsm > 0){
12781                 labelCfg.cls += ' col-sm-' + this.labelsm;
12782                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12783             }
12784             
12785             if(this.labelxs > 0){
12786                 labelCfg.cls += ' col-xs-' + this.labelxs;
12787                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12788             }
12789             
12790             
12791         } else if ( this.fieldLabel.length) {
12792                 
12793             
12794             
12795             cfg.cn = [
12796                 {
12797                     tag : 'i',
12798                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12799                     tooltip : 'This field is required',
12800                     style : this.allowBlank ? ' display:none' : '' 
12801                 },
12802                 {
12803                     tag: 'label',
12804                    //cls : 'input-group-addon',
12805                     html : this.fieldLabel
12806
12807                 },
12808
12809                inputblock
12810
12811            ];
12812            
12813            if(this.indicatorpos == 'right'){
12814        
12815                 cfg.cn = [
12816                     {
12817                         tag: 'label',
12818                        //cls : 'input-group-addon',
12819                         html : this.fieldLabel
12820
12821                     },
12822                     {
12823                         tag : 'i',
12824                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12825                         tooltip : 'This field is required',
12826                         style : this.allowBlank ? ' display:none' : '' 
12827                     },
12828
12829                    inputblock
12830
12831                ];
12832
12833             }
12834
12835         } else {
12836             
12837             cfg.cn = [
12838
12839                     inputblock
12840
12841             ];
12842                 
12843                 
12844         };
12845         return cfg;
12846     },
12847     
12848     
12849     /**
12850      * return the real input element.
12851      */
12852     inputEl: function ()
12853     {
12854         return this.el.select('input.form-control',true).first();
12855     },
12856     
12857     tooltipEl : function()
12858     {
12859         return this.inputEl();
12860     },
12861     
12862     indicatorEl : function()
12863     {
12864         if (Roo.bootstrap.version == 4) {
12865             return false; // not enabled in v4 yet.
12866         }
12867         
12868         var indicator = this.el.select('i.roo-required-indicator',true).first();
12869         
12870         if(!indicator){
12871             return false;
12872         }
12873         
12874         return indicator;
12875         
12876     },
12877     
12878     setDisabled : function(v)
12879     {
12880         var i  = this.inputEl().dom;
12881         if (!v) {
12882             i.removeAttribute('disabled');
12883             return;
12884             
12885         }
12886         i.setAttribute('disabled','true');
12887     },
12888     initEvents : function()
12889     {
12890           
12891         this.inputEl().on("keydown" , this.fireKey,  this);
12892         this.inputEl().on("focus", this.onFocus,  this);
12893         this.inputEl().on("blur", this.onBlur,  this);
12894         
12895         this.inputEl().relayEvent('keyup', this);
12896         this.inputEl().relayEvent('paste', this);
12897         
12898         this.indicator = this.indicatorEl();
12899         
12900         if(this.indicator){
12901             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12902         }
12903  
12904         // reference to original value for reset
12905         this.originalValue = this.getValue();
12906         //Roo.form.TextField.superclass.initEvents.call(this);
12907         if(this.validationEvent == 'keyup'){
12908             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12909             this.inputEl().on('keyup', this.filterValidation, this);
12910         }
12911         else if(this.validationEvent !== false){
12912             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12913         }
12914         
12915         if(this.selectOnFocus){
12916             this.on("focus", this.preFocus, this);
12917             
12918         }
12919         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12920             this.inputEl().on("keypress", this.filterKeys, this);
12921         } else {
12922             this.inputEl().relayEvent('keypress', this);
12923         }
12924        /* if(this.grow){
12925             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12926             this.el.on("click", this.autoSize,  this);
12927         }
12928         */
12929         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12930             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12931         }
12932         
12933         if (typeof(this.before) == 'object') {
12934             this.before.render(this.el.select('.roo-input-before',true).first());
12935         }
12936         if (typeof(this.after) == 'object') {
12937             this.after.render(this.el.select('.roo-input-after',true).first());
12938         }
12939         
12940         this.inputEl().on('change', this.onChange, this);
12941         
12942     },
12943     filterValidation : function(e){
12944         if(!e.isNavKeyPress()){
12945             this.validationTask.delay(this.validationDelay);
12946         }
12947     },
12948      /**
12949      * Validates the field value
12950      * @return {Boolean} True if the value is valid, else false
12951      */
12952     validate : function(){
12953         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12954         if(this.disabled || this.validateValue(this.getRawValue())){
12955             this.markValid();
12956             return true;
12957         }
12958         
12959         this.markInvalid();
12960         return false;
12961     },
12962     
12963     
12964     /**
12965      * Validates a value according to the field's validation rules and marks the field as invalid
12966      * if the validation fails
12967      * @param {Mixed} value The value to validate
12968      * @return {Boolean} True if the value is valid, else false
12969      */
12970     validateValue : function(value)
12971     {
12972         if(this.getVisibilityEl().hasClass('hidden')){
12973             return true;
12974         }
12975         
12976         if(value.length < 1)  { // if it's blank
12977             if(this.allowBlank){
12978                 return true;
12979             }
12980             return false;
12981         }
12982         
12983         if(value.length < this.minLength){
12984             return false;
12985         }
12986         if(value.length > this.maxLength){
12987             return false;
12988         }
12989         if(this.vtype){
12990             var vt = Roo.form.VTypes;
12991             if(!vt[this.vtype](value, this)){
12992                 return false;
12993             }
12994         }
12995         if(typeof this.validator == "function"){
12996             var msg = this.validator(value);
12997             if (typeof(msg) == 'string') {
12998                 this.invalidText = msg;
12999             }
13000             if(msg !== true){
13001                 return false;
13002             }
13003         }
13004         
13005         if(this.regex && !this.regex.test(value)){
13006             return false;
13007         }
13008         
13009         return true;
13010     },
13011     
13012      // private
13013     fireKey : function(e){
13014         //Roo.log('field ' + e.getKey());
13015         if(e.isNavKeyPress()){
13016             this.fireEvent("specialkey", this, e);
13017         }
13018     },
13019     focus : function (selectText){
13020         if(this.rendered){
13021             this.inputEl().focus();
13022             if(selectText === true){
13023                 this.inputEl().dom.select();
13024             }
13025         }
13026         return this;
13027     } ,
13028     
13029     onFocus : function(){
13030         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13031            // this.el.addClass(this.focusClass);
13032         }
13033         if(!this.hasFocus){
13034             this.hasFocus = true;
13035             this.startValue = this.getValue();
13036             this.fireEvent("focus", this);
13037         }
13038     },
13039     
13040     beforeBlur : Roo.emptyFn,
13041
13042     
13043     // private
13044     onBlur : function(){
13045         this.beforeBlur();
13046         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13047             //this.el.removeClass(this.focusClass);
13048         }
13049         this.hasFocus = false;
13050         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13051             this.validate();
13052         }
13053         var v = this.getValue();
13054         if(String(v) !== String(this.startValue)){
13055             this.fireEvent('change', this, v, this.startValue);
13056         }
13057         this.fireEvent("blur", this);
13058     },
13059     
13060     onChange : function(e)
13061     {
13062         var v = this.getValue();
13063         if(String(v) !== String(this.startValue)){
13064             this.fireEvent('change', this, v, this.startValue);
13065         }
13066         
13067     },
13068     
13069     /**
13070      * Resets the current field value to the originally loaded value and clears any validation messages
13071      */
13072     reset : function(){
13073         this.setValue(this.originalValue);
13074         this.validate();
13075     },
13076      /**
13077      * Returns the name of the field
13078      * @return {Mixed} name The name field
13079      */
13080     getName: function(){
13081         return this.name;
13082     },
13083      /**
13084      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13085      * @return {Mixed} value The field value
13086      */
13087     getValue : function(){
13088         
13089         var v = this.inputEl().getValue();
13090         
13091         return v;
13092     },
13093     /**
13094      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13095      * @return {Mixed} value The field value
13096      */
13097     getRawValue : function(){
13098         var v = this.inputEl().getValue();
13099         
13100         return v;
13101     },
13102     
13103     /**
13104      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13105      * @param {Mixed} value The value to set
13106      */
13107     setRawValue : function(v){
13108         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13109     },
13110     
13111     selectText : function(start, end){
13112         var v = this.getRawValue();
13113         if(v.length > 0){
13114             start = start === undefined ? 0 : start;
13115             end = end === undefined ? v.length : end;
13116             var d = this.inputEl().dom;
13117             if(d.setSelectionRange){
13118                 d.setSelectionRange(start, end);
13119             }else if(d.createTextRange){
13120                 var range = d.createTextRange();
13121                 range.moveStart("character", start);
13122                 range.moveEnd("character", v.length-end);
13123                 range.select();
13124             }
13125         }
13126     },
13127     
13128     /**
13129      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13130      * @param {Mixed} value The value to set
13131      */
13132     setValue : function(v){
13133         this.value = v;
13134         if(this.rendered){
13135             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13136             this.validate();
13137         }
13138     },
13139     
13140     /*
13141     processValue : function(value){
13142         if(this.stripCharsRe){
13143             var newValue = value.replace(this.stripCharsRe, '');
13144             if(newValue !== value){
13145                 this.setRawValue(newValue);
13146                 return newValue;
13147             }
13148         }
13149         return value;
13150     },
13151   */
13152     preFocus : function(){
13153         
13154         if(this.selectOnFocus){
13155             this.inputEl().dom.select();
13156         }
13157     },
13158     filterKeys : function(e){
13159         var k = e.getKey();
13160         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13161             return;
13162         }
13163         var c = e.getCharCode(), cc = String.fromCharCode(c);
13164         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13165             return;
13166         }
13167         if(!this.maskRe.test(cc)){
13168             e.stopEvent();
13169         }
13170     },
13171      /**
13172      * Clear any invalid styles/messages for this field
13173      */
13174     clearInvalid : function(){
13175         
13176         if(!this.el || this.preventMark){ // not rendered
13177             return;
13178         }
13179         
13180         
13181         this.el.removeClass([this.invalidClass, 'is-invalid']);
13182         
13183         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13184             
13185             var feedback = this.el.select('.form-control-feedback', true).first();
13186             
13187             if(feedback){
13188                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13189             }
13190             
13191         }
13192         
13193         if(this.indicator){
13194             this.indicator.removeClass('visible');
13195             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13196         }
13197         
13198         this.fireEvent('valid', this);
13199     },
13200     
13201      /**
13202      * Mark this field as valid
13203      */
13204     markValid : function()
13205     {
13206         if(!this.el  || this.preventMark){ // not rendered...
13207             return;
13208         }
13209         
13210         this.el.removeClass([this.invalidClass, this.validClass]);
13211         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13212
13213         var feedback = this.el.select('.form-control-feedback', true).first();
13214             
13215         if(feedback){
13216             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13217         }
13218         
13219         if(this.indicator){
13220             this.indicator.removeClass('visible');
13221             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13222         }
13223         
13224         if(this.disabled){
13225             return;
13226         }
13227         
13228            
13229         if(this.allowBlank && !this.getRawValue().length){
13230             return;
13231         }
13232         if (Roo.bootstrap.version == 3) {
13233             this.el.addClass(this.validClass);
13234         } else {
13235             this.inputEl().addClass('is-valid');
13236         }
13237
13238         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13239             
13240             var feedback = this.el.select('.form-control-feedback', true).first();
13241             
13242             if(feedback){
13243                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13244                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13245             }
13246             
13247         }
13248         
13249         this.fireEvent('valid', this);
13250     },
13251     
13252      /**
13253      * Mark this field as invalid
13254      * @param {String} msg The validation message
13255      */
13256     markInvalid : function(msg)
13257     {
13258         if(!this.el  || this.preventMark){ // not rendered
13259             return;
13260         }
13261         
13262         this.el.removeClass([this.invalidClass, this.validClass]);
13263         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13264         
13265         var feedback = this.el.select('.form-control-feedback', true).first();
13266             
13267         if(feedback){
13268             this.el.select('.form-control-feedback', true).first().removeClass(
13269                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13270         }
13271
13272         if(this.disabled){
13273             return;
13274         }
13275         
13276         if(this.allowBlank && !this.getRawValue().length){
13277             return;
13278         }
13279         
13280         if(this.indicator){
13281             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13282             this.indicator.addClass('visible');
13283         }
13284         if (Roo.bootstrap.version == 3) {
13285             this.el.addClass(this.invalidClass);
13286         } else {
13287             this.inputEl().addClass('is-invalid');
13288         }
13289         
13290         
13291         
13292         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13293             
13294             var feedback = this.el.select('.form-control-feedback', true).first();
13295             
13296             if(feedback){
13297                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13298                 
13299                 if(this.getValue().length || this.forceFeedback){
13300                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13301                 }
13302                 
13303             }
13304             
13305         }
13306         
13307         this.fireEvent('invalid', this, msg);
13308     },
13309     // private
13310     SafariOnKeyDown : function(event)
13311     {
13312         // this is a workaround for a password hang bug on chrome/ webkit.
13313         if (this.inputEl().dom.type != 'password') {
13314             return;
13315         }
13316         
13317         var isSelectAll = false;
13318         
13319         if(this.inputEl().dom.selectionEnd > 0){
13320             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13321         }
13322         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13323             event.preventDefault();
13324             this.setValue('');
13325             return;
13326         }
13327         
13328         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13329             
13330             event.preventDefault();
13331             // this is very hacky as keydown always get's upper case.
13332             //
13333             var cc = String.fromCharCode(event.getCharCode());
13334             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13335             
13336         }
13337     },
13338     adjustWidth : function(tag, w){
13339         tag = tag.toLowerCase();
13340         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13341             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13342                 if(tag == 'input'){
13343                     return w + 2;
13344                 }
13345                 if(tag == 'textarea'){
13346                     return w-2;
13347                 }
13348             }else if(Roo.isOpera){
13349                 if(tag == 'input'){
13350                     return w + 2;
13351                 }
13352                 if(tag == 'textarea'){
13353                     return w-2;
13354                 }
13355             }
13356         }
13357         return w;
13358     },
13359     
13360     setFieldLabel : function(v)
13361     {
13362         if(!this.rendered){
13363             return;
13364         }
13365         
13366         if(this.indicatorEl()){
13367             var ar = this.el.select('label > span',true);
13368             
13369             if (ar.elements.length) {
13370                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13371                 this.fieldLabel = v;
13372                 return;
13373             }
13374             
13375             var br = this.el.select('label',true);
13376             
13377             if(br.elements.length) {
13378                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13379                 this.fieldLabel = v;
13380                 return;
13381             }
13382             
13383             Roo.log('Cannot Found any of label > span || label in input');
13384             return;
13385         }
13386         
13387         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13388         this.fieldLabel = v;
13389         
13390         
13391     }
13392 });
13393
13394  
13395 /*
13396  * - LGPL
13397  *
13398  * Input
13399  * 
13400  */
13401
13402 /**
13403  * @class Roo.bootstrap.form.TextArea
13404  * @extends Roo.bootstrap.form.Input
13405  * Bootstrap TextArea class
13406  * @cfg {Number} cols Specifies the visible width of a text area
13407  * @cfg {Number} rows Specifies the visible number of lines in a text area
13408  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13409  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13410  * @cfg {string} html text
13411  * 
13412  * @constructor
13413  * Create a new TextArea
13414  * @param {Object} config The config object
13415  */
13416
13417 Roo.bootstrap.form.TextArea = function(config){
13418     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13419    
13420 };
13421
13422 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13423      
13424     cols : false,
13425     rows : 5,
13426     readOnly : false,
13427     warp : 'soft',
13428     resize : false,
13429     value: false,
13430     html: false,
13431     
13432     getAutoCreate : function(){
13433         
13434         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13435         
13436         var id = Roo.id();
13437         
13438         var cfg = {};
13439         
13440         if(this.inputType != 'hidden'){
13441             cfg.cls = 'form-group' //input-group
13442         }
13443         
13444         var input =  {
13445             tag: 'textarea',
13446             id : id,
13447             warp : this.warp,
13448             rows : this.rows,
13449             value : this.value || '',
13450             html: this.html || '',
13451             cls : 'form-control',
13452             placeholder : this.placeholder || '' 
13453             
13454         };
13455         
13456         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13457             input.maxLength = this.maxLength;
13458         }
13459         
13460         if(this.resize){
13461             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13462         }
13463         
13464         if(this.cols){
13465             input.cols = this.cols;
13466         }
13467         
13468         if (this.readOnly) {
13469             input.readonly = true;
13470         }
13471         
13472         if (this.name) {
13473             input.name = this.name;
13474         }
13475         
13476         if (this.size) {
13477             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13478         }
13479         
13480         var settings=this;
13481         ['xs','sm','md','lg'].map(function(size){
13482             if (settings[size]) {
13483                 cfg.cls += ' col-' + size + '-' + settings[size];
13484             }
13485         });
13486         
13487         var inputblock = input;
13488         
13489         if(this.hasFeedback && !this.allowBlank){
13490             
13491             var feedback = {
13492                 tag: 'span',
13493                 cls: 'glyphicon form-control-feedback'
13494             };
13495
13496             inputblock = {
13497                 cls : 'has-feedback',
13498                 cn :  [
13499                     input,
13500                     feedback
13501                 ] 
13502             };  
13503         }
13504         
13505         
13506         if (this.before || this.after) {
13507             
13508             inputblock = {
13509                 cls : 'input-group',
13510                 cn :  [] 
13511             };
13512             if (this.before) {
13513                 inputblock.cn.push({
13514                     tag :'span',
13515                     cls : 'input-group-addon',
13516                     html : this.before
13517                 });
13518             }
13519             
13520             inputblock.cn.push(input);
13521             
13522             if(this.hasFeedback && !this.allowBlank){
13523                 inputblock.cls += ' has-feedback';
13524                 inputblock.cn.push(feedback);
13525             }
13526             
13527             if (this.after) {
13528                 inputblock.cn.push({
13529                     tag :'span',
13530                     cls : 'input-group-addon',
13531                     html : this.after
13532                 });
13533             }
13534             
13535         }
13536         
13537         
13538         cfg = this.getAutoCreateLabel( cfg, inputblock );
13539
13540          
13541         
13542         if (this.disabled) {
13543             input.disabled=true;
13544         }
13545         
13546         return cfg;
13547         
13548     },
13549     /**
13550      * return the real textarea element.
13551      */
13552     inputEl: function ()
13553     {
13554         return this.el.select('textarea.form-control',true).first();
13555     },
13556     
13557     /**
13558      * Clear any invalid styles/messages for this field
13559      */
13560     clearInvalid : function()
13561     {
13562         
13563         if(!this.el || this.preventMark){ // not rendered
13564             return;
13565         }
13566         
13567         var label = this.el.select('label', true).first();
13568         //var icon = this.el.select('i.fa-star', true).first();
13569         
13570         //if(label && icon){
13571         //    icon.remove();
13572         //}
13573         this.el.removeClass( this.validClass);
13574         this.inputEl().removeClass('is-invalid');
13575          
13576         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13577             
13578             var feedback = this.el.select('.form-control-feedback', true).first();
13579             
13580             if(feedback){
13581                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13582             }
13583             
13584         }
13585         
13586         this.fireEvent('valid', this);
13587     },
13588     
13589      /**
13590      * Mark this field as valid
13591      */
13592     markValid : function()
13593     {
13594         if(!this.el  || this.preventMark){ // not rendered
13595             return;
13596         }
13597         
13598         this.el.removeClass([this.invalidClass, this.validClass]);
13599         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13600         
13601         var feedback = this.el.select('.form-control-feedback', true).first();
13602             
13603         if(feedback){
13604             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13605         }
13606
13607         if(this.disabled || this.allowBlank){
13608             return;
13609         }
13610         
13611         var label = this.el.select('label', true).first();
13612         var icon = this.el.select('i.fa-star', true).first();
13613         
13614         //if(label && icon){
13615         //    icon.remove();
13616         //}
13617         if (Roo.bootstrap.version == 3) {
13618             this.el.addClass(this.validClass);
13619         } else {
13620             this.inputEl().addClass('is-valid');
13621         }
13622         
13623         
13624         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13625             
13626             var feedback = this.el.select('.form-control-feedback', true).first();
13627             
13628             if(feedback){
13629                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13630                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13631             }
13632             
13633         }
13634         
13635         this.fireEvent('valid', this);
13636     },
13637     
13638      /**
13639      * Mark this field as invalid
13640      * @param {String} msg The validation message
13641      */
13642     markInvalid : function(msg)
13643     {
13644         if(!this.el  || this.preventMark){ // not rendered
13645             return;
13646         }
13647         
13648         this.el.removeClass([this.invalidClass, this.validClass]);
13649         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13650         
13651         var feedback = this.el.select('.form-control-feedback', true).first();
13652             
13653         if(feedback){
13654             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13655         }
13656
13657         if(this.disabled || this.allowBlank){
13658             return;
13659         }
13660         
13661         var label = this.el.select('label', true).first();
13662         //var icon = this.el.select('i.fa-star', true).first();
13663         
13664         //if(!this.getValue().length && label && !icon){
13665           /*  this.el.createChild({
13666                 tag : 'i',
13667                 cls : 'text-danger fa fa-lg fa-star',
13668                 tooltip : 'This field is required',
13669                 style : 'margin-right:5px;'
13670             }, label, true);
13671             */
13672         //}
13673         
13674         if (Roo.bootstrap.version == 3) {
13675             this.el.addClass(this.invalidClass);
13676         } else {
13677             this.inputEl().addClass('is-invalid');
13678         }
13679         
13680         // fixme ... this may be depricated need to test..
13681         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13682             
13683             var feedback = this.el.select('.form-control-feedback', true).first();
13684             
13685             if(feedback){
13686                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13687                 
13688                 if(this.getValue().length || this.forceFeedback){
13689                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13690                 }
13691                 
13692             }
13693             
13694         }
13695         
13696         this.fireEvent('invalid', this, msg);
13697     }
13698 });
13699
13700  
13701 /*
13702  * - LGPL
13703  *
13704  * trigger field - base class for combo..
13705  * 
13706  */
13707  
13708 /**
13709  * @class Roo.bootstrap.form.TriggerField
13710  * @extends Roo.bootstrap.form.Input
13711  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13712  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13713  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13714  * for which you can provide a custom implementation.  For example:
13715  * <pre><code>
13716 var trigger = new Roo.bootstrap.form.TriggerField();
13717 trigger.onTriggerClick = myTriggerFn;
13718 trigger.applyTo('my-field');
13719 </code></pre>
13720  *
13721  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13722  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13723  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13724  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13725  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13726
13727  * @constructor
13728  * Create a new TriggerField.
13729  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13730  * to the base TextField)
13731  */
13732 Roo.bootstrap.form.TriggerField = function(config){
13733     this.mimicing = false;
13734     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13735 };
13736
13737 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13738     /**
13739      * @cfg {String} triggerClass A CSS class to apply to the trigger
13740      */
13741      /**
13742      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13743      */
13744     hideTrigger:false,
13745
13746     /**
13747      * @cfg {Boolean} removable (true|false) special filter default false
13748      */
13749     removable : false,
13750     
13751     /** @cfg {Boolean} grow @hide */
13752     /** @cfg {Number} growMin @hide */
13753     /** @cfg {Number} growMax @hide */
13754
13755     /**
13756      * @hide 
13757      * @method
13758      */
13759     autoSize: Roo.emptyFn,
13760     // private
13761     monitorTab : true,
13762     // private
13763     deferHeight : true,
13764
13765     
13766     actionMode : 'wrap',
13767     
13768     caret : false,
13769     
13770     
13771     getAutoCreate : function(){
13772        
13773         var align = this.labelAlign || this.parentLabelAlign();
13774         
13775         var id = Roo.id();
13776         
13777         var cfg = {
13778             cls: 'form-group' //input-group
13779         };
13780         
13781         
13782         var input =  {
13783             tag: 'input',
13784             id : id,
13785             type : this.inputType,
13786             cls : 'form-control',
13787             autocomplete: 'new-password',
13788             placeholder : this.placeholder || '' 
13789             
13790         };
13791         if (this.name) {
13792             input.name = this.name;
13793         }
13794         if (this.size) {
13795             input.cls += ' input-' + this.size;
13796         }
13797         
13798         if (this.disabled) {
13799             input.disabled=true;
13800         }
13801         
13802         var inputblock = input;
13803         
13804         if(this.hasFeedback && !this.allowBlank){
13805             
13806             var feedback = {
13807                 tag: 'span',
13808                 cls: 'glyphicon form-control-feedback'
13809             };
13810             
13811             if(this.removable && !this.editable  ){
13812                 inputblock = {
13813                     cls : 'has-feedback',
13814                     cn :  [
13815                         inputblock,
13816                         {
13817                             tag: 'button',
13818                             html : 'x',
13819                             cls : 'roo-combo-removable-btn close'
13820                         },
13821                         feedback
13822                     ] 
13823                 };
13824             } else {
13825                 inputblock = {
13826                     cls : 'has-feedback',
13827                     cn :  [
13828                         inputblock,
13829                         feedback
13830                     ] 
13831                 };
13832             }
13833
13834         } else {
13835             if(this.removable && !this.editable ){
13836                 inputblock = {
13837                     cls : 'roo-removable',
13838                     cn :  [
13839                         inputblock,
13840                         {
13841                             tag: 'button',
13842                             html : 'x',
13843                             cls : 'roo-combo-removable-btn close'
13844                         }
13845                     ] 
13846                 };
13847             }
13848         }
13849         
13850         if (this.before || this.after) {
13851             
13852             inputblock = {
13853                 cls : 'input-group',
13854                 cn :  [] 
13855             };
13856             if (this.before) {
13857                 inputblock.cn.push({
13858                     tag :'span',
13859                     cls : 'input-group-addon input-group-prepend input-group-text',
13860                     html : this.before
13861                 });
13862             }
13863             
13864             inputblock.cn.push(input);
13865             
13866             if(this.hasFeedback && !this.allowBlank){
13867                 inputblock.cls += ' has-feedback';
13868                 inputblock.cn.push(feedback);
13869             }
13870             
13871             if (this.after) {
13872                 inputblock.cn.push({
13873                     tag :'span',
13874                     cls : 'input-group-addon input-group-append input-group-text',
13875                     html : this.after
13876                 });
13877             }
13878             
13879         };
13880         
13881       
13882         
13883         var ibwrap = inputblock;
13884         
13885         if(this.multiple){
13886             ibwrap = {
13887                 tag: 'ul',
13888                 cls: 'roo-select2-choices',
13889                 cn:[
13890                     {
13891                         tag: 'li',
13892                         cls: 'roo-select2-search-field',
13893                         cn: [
13894
13895                             inputblock
13896                         ]
13897                     }
13898                 ]
13899             };
13900                 
13901         }
13902         
13903         var combobox = {
13904             cls: 'roo-select2-container input-group',
13905             cn: [
13906                  {
13907                     tag: 'input',
13908                     type : 'hidden',
13909                     cls: 'form-hidden-field'
13910                 },
13911                 ibwrap
13912             ]
13913         };
13914         
13915         if(!this.multiple && this.showToggleBtn){
13916             
13917             var caret = {
13918                         tag: 'span',
13919                         cls: 'caret'
13920              };
13921             if (this.caret != false) {
13922                 caret = {
13923                      tag: 'i',
13924                      cls: 'fa fa-' + this.caret
13925                 };
13926                 
13927             }
13928             
13929             combobox.cn.push({
13930                 tag :'span',
13931                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13932                 cn : [
13933                     Roo.bootstrap.version == 3 ? caret : '',
13934                     {
13935                         tag: 'span',
13936                         cls: 'combobox-clear',
13937                         cn  : [
13938                             {
13939                                 tag : 'i',
13940                                 cls: 'icon-remove'
13941                             }
13942                         ]
13943                     }
13944                 ]
13945
13946             })
13947         }
13948         
13949         if(this.multiple){
13950             combobox.cls += ' roo-select2-container-multi';
13951         }
13952          var indicator = {
13953             tag : 'i',
13954             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13955             tooltip : 'This field is required'
13956         };
13957       
13958         if (this.allowBlank) {
13959             indicator = {
13960                 tag : 'i',
13961                 style : 'display:none'
13962             };
13963         }
13964          
13965         
13966         
13967         if (align ==='left' && this.fieldLabel.length) {
13968             
13969             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13970
13971             cfg.cn = [
13972                 indicator,
13973                 {
13974                     tag: 'label',
13975                     'for' :  id,
13976                     cls : 'control-label',
13977                     html : this.fieldLabel
13978
13979                 },
13980                 {
13981                     cls : "", 
13982                     cn: [
13983                         combobox
13984                     ]
13985                 }
13986
13987             ];
13988             
13989             var labelCfg = cfg.cn[1];
13990             var contentCfg = cfg.cn[2];
13991             
13992             if(this.indicatorpos == 'right'){
13993                 cfg.cn = [
13994                     {
13995                         tag: 'label',
13996                         'for' :  id,
13997                         cls : 'control-label',
13998                         cn : [
13999                             {
14000                                 tag : 'span',
14001                                 html : this.fieldLabel
14002                             },
14003                             indicator
14004                         ]
14005                     },
14006                     {
14007                         cls : "", 
14008                         cn: [
14009                             combobox
14010                         ]
14011                     }
14012
14013                 ];
14014                 
14015                 labelCfg = cfg.cn[0];
14016                 contentCfg = cfg.cn[1];
14017             }
14018             
14019             if(this.labelWidth > 12){
14020                 labelCfg.style = "width: " + this.labelWidth + 'px';
14021             }
14022             
14023             if(this.labelWidth < 13 && this.labelmd == 0){
14024                 this.labelmd = this.labelWidth;
14025             }
14026             
14027             if(this.labellg > 0){
14028                 labelCfg.cls += ' col-lg-' + this.labellg;
14029                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14030             }
14031             
14032             if(this.labelmd > 0){
14033                 labelCfg.cls += ' col-md-' + this.labelmd;
14034                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14035             }
14036             
14037             if(this.labelsm > 0){
14038                 labelCfg.cls += ' col-sm-' + this.labelsm;
14039                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14040             }
14041             
14042             if(this.labelxs > 0){
14043                 labelCfg.cls += ' col-xs-' + this.labelxs;
14044                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14045             }
14046             
14047         } else if ( this.fieldLabel.length) {
14048 //                Roo.log(" label");
14049             cfg.cn = [
14050                 indicator,
14051                {
14052                    tag: 'label',
14053                    //cls : 'input-group-addon',
14054                    html : this.fieldLabel
14055
14056                },
14057
14058                combobox
14059
14060             ];
14061             
14062             if(this.indicatorpos == 'right'){
14063                 
14064                 cfg.cn = [
14065                     {
14066                        tag: 'label',
14067                        cn : [
14068                            {
14069                                tag : 'span',
14070                                html : this.fieldLabel
14071                            },
14072                            indicator
14073                        ]
14074
14075                     },
14076                     combobox
14077
14078                 ];
14079
14080             }
14081
14082         } else {
14083             
14084 //                Roo.log(" no label && no align");
14085                 cfg = combobox
14086                      
14087                 
14088         }
14089         
14090         var settings=this;
14091         ['xs','sm','md','lg'].map(function(size){
14092             if (settings[size]) {
14093                 cfg.cls += ' col-' + size + '-' + settings[size];
14094             }
14095         });
14096         
14097         return cfg;
14098         
14099     },
14100     
14101     
14102     
14103     // private
14104     onResize : function(w, h){
14105 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14106 //        if(typeof w == 'number'){
14107 //            var x = w - this.trigger.getWidth();
14108 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14109 //            this.trigger.setStyle('left', x+'px');
14110 //        }
14111     },
14112
14113     // private
14114     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14115
14116     // private
14117     getResizeEl : function(){
14118         return this.inputEl();
14119     },
14120
14121     // private
14122     getPositionEl : function(){
14123         return this.inputEl();
14124     },
14125
14126     // private
14127     alignErrorIcon : function(){
14128         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14129     },
14130
14131     // private
14132     initEvents : function(){
14133         
14134         this.createList();
14135         
14136         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14137         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14138         if(!this.multiple && this.showToggleBtn){
14139             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14140             if(this.hideTrigger){
14141                 this.trigger.setDisplayed(false);
14142             }
14143             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14144         }
14145         
14146         if(this.multiple){
14147             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14148         }
14149         
14150         if(this.removable && !this.editable && !this.tickable){
14151             var close = this.closeTriggerEl();
14152             
14153             if(close){
14154                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14155                 close.on('click', this.removeBtnClick, this, close);
14156             }
14157         }
14158         
14159         //this.trigger.addClassOnOver('x-form-trigger-over');
14160         //this.trigger.addClassOnClick('x-form-trigger-click');
14161         
14162         //if(!this.width){
14163         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14164         //}
14165     },
14166     
14167     closeTriggerEl : function()
14168     {
14169         var close = this.el.select('.roo-combo-removable-btn', true).first();
14170         return close ? close : false;
14171     },
14172     
14173     removeBtnClick : function(e, h, el)
14174     {
14175         e.preventDefault();
14176         
14177         if(this.fireEvent("remove", this) !== false){
14178             this.reset();
14179             this.fireEvent("afterremove", this)
14180         }
14181     },
14182     
14183     createList : function()
14184     {
14185         this.list = Roo.get(document.body).createChild({
14186             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14187             cls: 'typeahead typeahead-long dropdown-menu shadow',
14188             style: 'display:none'
14189         });
14190         
14191         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14192         
14193     },
14194
14195     // private
14196     initTrigger : function(){
14197        
14198     },
14199
14200     // private
14201     onDestroy : function(){
14202         if(this.trigger){
14203             this.trigger.removeAllListeners();
14204           //  this.trigger.remove();
14205         }
14206         //if(this.wrap){
14207         //    this.wrap.remove();
14208         //}
14209         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14210     },
14211
14212     // private
14213     onFocus : function(){
14214         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14215         /*
14216         if(!this.mimicing){
14217             this.wrap.addClass('x-trigger-wrap-focus');
14218             this.mimicing = true;
14219             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14220             if(this.monitorTab){
14221                 this.el.on("keydown", this.checkTab, this);
14222             }
14223         }
14224         */
14225     },
14226
14227     // private
14228     checkTab : function(e){
14229         if(e.getKey() == e.TAB){
14230             this.triggerBlur();
14231         }
14232     },
14233
14234     // private
14235     onBlur : function(){
14236         // do nothing
14237     },
14238
14239     // private
14240     mimicBlur : function(e, t){
14241         /*
14242         if(!this.wrap.contains(t) && this.validateBlur()){
14243             this.triggerBlur();
14244         }
14245         */
14246     },
14247
14248     // private
14249     triggerBlur : function(){
14250         this.mimicing = false;
14251         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14252         if(this.monitorTab){
14253             this.el.un("keydown", this.checkTab, this);
14254         }
14255         //this.wrap.removeClass('x-trigger-wrap-focus');
14256         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14257     },
14258
14259     // private
14260     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14261     validateBlur : function(e, t){
14262         return true;
14263     },
14264
14265     // private
14266     onDisable : function(){
14267         this.inputEl().dom.disabled = true;
14268         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14269         //if(this.wrap){
14270         //    this.wrap.addClass('x-item-disabled');
14271         //}
14272     },
14273
14274     // private
14275     onEnable : function(){
14276         this.inputEl().dom.disabled = false;
14277         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14278         //if(this.wrap){
14279         //    this.el.removeClass('x-item-disabled');
14280         //}
14281     },
14282
14283     // private
14284     onShow : function(){
14285         var ae = this.getActionEl();
14286         
14287         if(ae){
14288             ae.dom.style.display = '';
14289             ae.dom.style.visibility = 'visible';
14290         }
14291     },
14292
14293     // private
14294     
14295     onHide : function(){
14296         var ae = this.getActionEl();
14297         ae.dom.style.display = 'none';
14298     },
14299
14300     /**
14301      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14302      * by an implementing function.
14303      * @method
14304      * @param {EventObject} e
14305      */
14306     onTriggerClick : Roo.emptyFn
14307 });
14308  
14309 /*
14310 * Licence: LGPL
14311 */
14312
14313 /**
14314  * @class Roo.bootstrap.form.CardUploader
14315  * @extends Roo.bootstrap.Button
14316  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14317  * @cfg {Number} errorTimeout default 3000
14318  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14319  * @cfg {Array}  html The button text.
14320
14321  *
14322  * @constructor
14323  * Create a new CardUploader
14324  * @param {Object} config The config object
14325  */
14326
14327 Roo.bootstrap.form.CardUploader = function(config){
14328     
14329  
14330     
14331     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14332     
14333     
14334     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14335         return r.data.id
14336      });
14337     
14338      this.addEvents({
14339          // raw events
14340         /**
14341          * @event preview
14342          * When a image is clicked on - and needs to display a slideshow or similar..
14343          * @param {Roo.bootstrap.Card} this
14344          * @param {Object} The image information data 
14345          *
14346          */
14347         'preview' : true,
14348          /**
14349          * @event download
14350          * When a the download link is clicked
14351          * @param {Roo.bootstrap.Card} this
14352          * @param {Object} The image information data  contains 
14353          */
14354         'download' : true
14355         
14356     });
14357 };
14358  
14359 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14360     
14361      
14362     errorTimeout : 3000,
14363      
14364     images : false,
14365    
14366     fileCollection : false,
14367     allowBlank : true,
14368     
14369     getAutoCreate : function()
14370     {
14371         
14372         var cfg =  {
14373             cls :'form-group' ,
14374             cn : [
14375                
14376                 {
14377                     tag: 'label',
14378                    //cls : 'input-group-addon',
14379                     html : this.fieldLabel
14380
14381                 },
14382
14383                 {
14384                     tag: 'input',
14385                     type : 'hidden',
14386                     name : this.name,
14387                     value : this.value,
14388                     cls : 'd-none  form-control'
14389                 },
14390                 
14391                 {
14392                     tag: 'input',
14393                     multiple : 'multiple',
14394                     type : 'file',
14395                     cls : 'd-none  roo-card-upload-selector'
14396                 },
14397                 
14398                 {
14399                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14400                 },
14401                 {
14402                     cls : 'card-columns roo-card-uploader-container'
14403                 }
14404
14405             ]
14406         };
14407            
14408          
14409         return cfg;
14410     },
14411     
14412     getChildContainer : function() /// what children are added to.
14413     {
14414         return this.containerEl;
14415     },
14416    
14417     getButtonContainer : function() /// what children are added to.
14418     {
14419         return this.el.select(".roo-card-uploader-button-container").first();
14420     },
14421    
14422     initEvents : function()
14423     {
14424         
14425         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14426         
14427         var t = this;
14428         this.addxtype({
14429             xns: Roo.bootstrap,
14430
14431             xtype : 'Button',
14432             container_method : 'getButtonContainer' ,            
14433             html :  this.html, // fix changable?
14434             cls : 'w-100 ',
14435             listeners : {
14436                 'click' : function(btn, e) {
14437                     t.onClick(e);
14438                 }
14439             }
14440         });
14441         
14442         
14443         
14444         
14445         this.urlAPI = (window.createObjectURL && window) || 
14446                                 (window.URL && URL.revokeObjectURL && URL) || 
14447                                 (window.webkitURL && webkitURL);
14448                         
14449          
14450          
14451          
14452         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14453         
14454         this.selectorEl.on('change', this.onFileSelected, this);
14455         if (this.images) {
14456             var t = this;
14457             this.images.forEach(function(img) {
14458                 t.addCard(img)
14459             });
14460             this.images = false;
14461         }
14462         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14463          
14464        
14465     },
14466     
14467    
14468     onClick : function(e)
14469     {
14470         e.preventDefault();
14471          
14472         this.selectorEl.dom.click();
14473          
14474     },
14475     
14476     onFileSelected : function(e)
14477     {
14478         e.preventDefault();
14479         
14480         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14481             return;
14482         }
14483         
14484         Roo.each(this.selectorEl.dom.files, function(file){    
14485             this.addFile(file);
14486         }, this);
14487          
14488     },
14489     
14490       
14491     
14492       
14493     
14494     addFile : function(file)
14495     {
14496            
14497         if(typeof(file) === 'string'){
14498             throw "Add file by name?"; // should not happen
14499             return;
14500         }
14501         
14502         if(!file || !this.urlAPI){
14503             return;
14504         }
14505         
14506         // file;
14507         // file.type;
14508         
14509         var _this = this;
14510         
14511         
14512         var url = _this.urlAPI.createObjectURL( file);
14513            
14514         this.addCard({
14515             id : Roo.bootstrap.form.CardUploader.ID--,
14516             is_uploaded : false,
14517             src : url,
14518             srcfile : file,
14519             title : file.name,
14520             mimetype : file.type,
14521             preview : false,
14522             is_deleted : 0
14523         });
14524         
14525     },
14526     
14527     /**
14528      * addCard - add an Attachment to the uploader
14529      * @param data - the data about the image to upload
14530      *
14531      * {
14532           id : 123
14533           title : "Title of file",
14534           is_uploaded : false,
14535           src : "http://.....",
14536           srcfile : { the File upload object },
14537           mimetype : file.type,
14538           preview : false,
14539           is_deleted : 0
14540           .. any other data...
14541         }
14542      *
14543      * 
14544     */
14545     
14546     addCard : function (data)
14547     {
14548         // hidden input element?
14549         // if the file is not an image...
14550         //then we need to use something other that and header_image
14551         var t = this;
14552         //   remove.....
14553         var footer = [
14554             {
14555                 xns : Roo.bootstrap,
14556                 xtype : 'CardFooter',
14557                  items: [
14558                     {
14559                         xns : Roo.bootstrap,
14560                         xtype : 'Element',
14561                         cls : 'd-flex',
14562                         items : [
14563                             
14564                             {
14565                                 xns : Roo.bootstrap,
14566                                 xtype : 'Button',
14567                                 html : String.format("<small>{0}</small>", data.title),
14568                                 cls : 'col-10 text-left',
14569                                 size: 'sm',
14570                                 weight: 'link',
14571                                 fa : 'download',
14572                                 listeners : {
14573                                     click : function() {
14574                                      
14575                                         t.fireEvent( "download", t, data );
14576                                     }
14577                                 }
14578                             },
14579                           
14580                             {
14581                                 xns : Roo.bootstrap,
14582                                 xtype : 'Button',
14583                                 style: 'max-height: 28px; ',
14584                                 size : 'sm',
14585                                 weight: 'danger',
14586                                 cls : 'col-2',
14587                                 fa : 'times',
14588                                 listeners : {
14589                                     click : function() {
14590                                         t.removeCard(data.id)
14591                                     }
14592                                 }
14593                             }
14594                         ]
14595                     }
14596                     
14597                 ] 
14598             }
14599             
14600         ];
14601         
14602         var cn = this.addxtype(
14603             {
14604                  
14605                 xns : Roo.bootstrap,
14606                 xtype : 'Card',
14607                 closeable : true,
14608                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14609                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14610                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14611                 data : data,
14612                 html : false,
14613                  
14614                 items : footer,
14615                 initEvents : function() {
14616                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14617                     var card = this;
14618                     this.imgEl = this.el.select('.card-img-top').first();
14619                     if (this.imgEl) {
14620                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14621                         this.imgEl.set({ 'pointer' : 'cursor' });
14622                                   
14623                     }
14624                     this.getCardFooter().addClass('p-1');
14625                     
14626                   
14627                 }
14628                 
14629             }
14630         );
14631         // dont' really need ot update items.
14632         // this.items.push(cn);
14633         this.fileCollection.add(cn);
14634         
14635         if (!data.srcfile) {
14636             this.updateInput();
14637             return;
14638         }
14639             
14640         var _t = this;
14641         var reader = new FileReader();
14642         reader.addEventListener("load", function() {  
14643             data.srcdata =  reader.result;
14644             _t.updateInput();
14645         });
14646         reader.readAsDataURL(data.srcfile);
14647         
14648         
14649         
14650     },
14651     removeCard : function(id)
14652     {
14653         
14654         var card  = this.fileCollection.get(id);
14655         card.data.is_deleted = 1;
14656         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14657         //this.fileCollection.remove(card);
14658         //this.items = this.items.filter(function(e) { return e != card });
14659         // dont' really need ot update items.
14660         card.el.dom.parentNode.removeChild(card.el.dom);
14661         this.updateInput();
14662
14663         
14664     },
14665     reset: function()
14666     {
14667         this.fileCollection.each(function(card) {
14668             if (card.el.dom && card.el.dom.parentNode) {
14669                 card.el.dom.parentNode.removeChild(card.el.dom);
14670             }
14671         });
14672         this.fileCollection.clear();
14673         this.updateInput();
14674     },
14675     
14676     updateInput : function()
14677     {
14678          var data = [];
14679         this.fileCollection.each(function(e) {
14680             data.push(e.data);
14681             
14682         });
14683         this.inputEl().dom.value = JSON.stringify(data);
14684         
14685         
14686         
14687     }
14688     
14689     
14690 });
14691
14692
14693 Roo.bootstrap.form.CardUploader.ID = -1;/*
14694  * Based on:
14695  * Ext JS Library 1.1.1
14696  * Copyright(c) 2006-2007, Ext JS, LLC.
14697  *
14698  * Originally Released Under LGPL - original licence link has changed is not relivant.
14699  *
14700  * Fork - LGPL
14701  * <script type="text/javascript">
14702  */
14703
14704
14705 /**
14706  * @class Roo.data.SortTypes
14707  * @static
14708  * Defines the default sorting (casting?) comparison functions used when sorting data.
14709  */
14710 Roo.data.SortTypes = {
14711     /**
14712      * Default sort that does nothing
14713      * @param {Mixed} s The value being converted
14714      * @return {Mixed} The comparison value
14715      */
14716     none : function(s){
14717         return s;
14718     },
14719     
14720     /**
14721      * The regular expression used to strip tags
14722      * @type {RegExp}
14723      * @property
14724      */
14725     stripTagsRE : /<\/?[^>]+>/gi,
14726     
14727     /**
14728      * Strips all HTML tags to sort on text only
14729      * @param {Mixed} s The value being converted
14730      * @return {String} The comparison value
14731      */
14732     asText : function(s){
14733         return String(s).replace(this.stripTagsRE, "");
14734     },
14735     
14736     /**
14737      * Strips all HTML tags to sort on text only - Case insensitive
14738      * @param {Mixed} s The value being converted
14739      * @return {String} The comparison value
14740      */
14741     asUCText : function(s){
14742         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14743     },
14744     
14745     /**
14746      * Case insensitive string
14747      * @param {Mixed} s The value being converted
14748      * @return {String} The comparison value
14749      */
14750     asUCString : function(s) {
14751         return String(s).toUpperCase();
14752     },
14753     
14754     /**
14755      * Date sorting
14756      * @param {Mixed} s The value being converted
14757      * @return {Number} The comparison value
14758      */
14759     asDate : function(s) {
14760         if(!s){
14761             return 0;
14762         }
14763         if(s instanceof Date){
14764             return s.getTime();
14765         }
14766         return Date.parse(String(s));
14767     },
14768     
14769     /**
14770      * Float sorting
14771      * @param {Mixed} s The value being converted
14772      * @return {Float} The comparison value
14773      */
14774     asFloat : function(s) {
14775         var val = parseFloat(String(s).replace(/,/g, ""));
14776         if(isNaN(val)) {
14777             val = 0;
14778         }
14779         return val;
14780     },
14781     
14782     /**
14783      * Integer sorting
14784      * @param {Mixed} s The value being converted
14785      * @return {Number} The comparison value
14786      */
14787     asInt : function(s) {
14788         var val = parseInt(String(s).replace(/,/g, ""));
14789         if(isNaN(val)) {
14790             val = 0;
14791         }
14792         return val;
14793     }
14794 };/*
14795  * Based on:
14796  * Ext JS Library 1.1.1
14797  * Copyright(c) 2006-2007, Ext JS, LLC.
14798  *
14799  * Originally Released Under LGPL - original licence link has changed is not relivant.
14800  *
14801  * Fork - LGPL
14802  * <script type="text/javascript">
14803  */
14804
14805 /**
14806 * @class Roo.data.Record
14807  * Instances of this class encapsulate both record <em>definition</em> information, and record
14808  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14809  * to access Records cached in an {@link Roo.data.Store} object.<br>
14810  * <p>
14811  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14812  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14813  * objects.<br>
14814  * <p>
14815  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14816  * @constructor
14817  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14818  * {@link #create}. The parameters are the same.
14819  * @param {Array} data An associative Array of data values keyed by the field name.
14820  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14821  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14822  * not specified an integer id is generated.
14823  */
14824 Roo.data.Record = function(data, id){
14825     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14826     this.data = data;
14827 };
14828
14829 /**
14830  * Generate a constructor for a specific record layout.
14831  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14832  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14833  * Each field definition object may contain the following properties: <ul>
14834  * <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,
14835  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14836  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14837  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14838  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14839  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14840  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14841  * this may be omitted.</p></li>
14842  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14843  * <ul><li>auto (Default, implies no conversion)</li>
14844  * <li>string</li>
14845  * <li>int</li>
14846  * <li>float</li>
14847  * <li>boolean</li>
14848  * <li>date</li></ul></p></li>
14849  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14850  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14851  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14852  * by the Reader into an object that will be stored in the Record. It is passed the
14853  * following parameters:<ul>
14854  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14855  * </ul></p></li>
14856  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14857  * </ul>
14858  * <br>usage:<br><pre><code>
14859 var TopicRecord = Roo.data.Record.create(
14860     {name: 'title', mapping: 'topic_title'},
14861     {name: 'author', mapping: 'username'},
14862     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14863     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14864     {name: 'lastPoster', mapping: 'user2'},
14865     {name: 'excerpt', mapping: 'post_text'}
14866 );
14867
14868 var myNewRecord = new TopicRecord({
14869     title: 'Do my job please',
14870     author: 'noobie',
14871     totalPosts: 1,
14872     lastPost: new Date(),
14873     lastPoster: 'Animal',
14874     excerpt: 'No way dude!'
14875 });
14876 myStore.add(myNewRecord);
14877 </code></pre>
14878  * @method create
14879  * @static
14880  */
14881 Roo.data.Record.create = function(o){
14882     var f = function(){
14883         f.superclass.constructor.apply(this, arguments);
14884     };
14885     Roo.extend(f, Roo.data.Record);
14886     var p = f.prototype;
14887     p.fields = new Roo.util.MixedCollection(false, function(field){
14888         return field.name;
14889     });
14890     for(var i = 0, len = o.length; i < len; i++){
14891         p.fields.add(new Roo.data.Field(o[i]));
14892     }
14893     f.getField = function(name){
14894         return p.fields.get(name);  
14895     };
14896     return f;
14897 };
14898
14899 Roo.data.Record.AUTO_ID = 1000;
14900 Roo.data.Record.EDIT = 'edit';
14901 Roo.data.Record.REJECT = 'reject';
14902 Roo.data.Record.COMMIT = 'commit';
14903
14904 Roo.data.Record.prototype = {
14905     /**
14906      * Readonly flag - true if this record has been modified.
14907      * @type Boolean
14908      */
14909     dirty : false,
14910     editing : false,
14911     error: null,
14912     modified: null,
14913
14914     // private
14915     join : function(store){
14916         this.store = store;
14917     },
14918
14919     /**
14920      * Set the named field to the specified value.
14921      * @param {String} name The name of the field to set.
14922      * @param {Object} value The value to set the field to.
14923      */
14924     set : function(name, value){
14925         if(this.data[name] == value){
14926             return;
14927         }
14928         this.dirty = true;
14929         if(!this.modified){
14930             this.modified = {};
14931         }
14932         if(typeof this.modified[name] == 'undefined'){
14933             this.modified[name] = this.data[name];
14934         }
14935         this.data[name] = value;
14936         if(!this.editing && this.store){
14937             this.store.afterEdit(this);
14938         }       
14939     },
14940
14941     /**
14942      * Get the value of the named field.
14943      * @param {String} name The name of the field to get the value of.
14944      * @return {Object} The value of the field.
14945      */
14946     get : function(name){
14947         return this.data[name]; 
14948     },
14949
14950     // private
14951     beginEdit : function(){
14952         this.editing = true;
14953         this.modified = {}; 
14954     },
14955
14956     // private
14957     cancelEdit : function(){
14958         this.editing = false;
14959         delete this.modified;
14960     },
14961
14962     // private
14963     endEdit : function(){
14964         this.editing = false;
14965         if(this.dirty && this.store){
14966             this.store.afterEdit(this);
14967         }
14968     },
14969
14970     /**
14971      * Usually called by the {@link Roo.data.Store} which owns the Record.
14972      * Rejects all changes made to the Record since either creation, or the last commit operation.
14973      * Modified fields are reverted to their original values.
14974      * <p>
14975      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14976      * of reject operations.
14977      */
14978     reject : function(){
14979         var m = this.modified;
14980         for(var n in m){
14981             if(typeof m[n] != "function"){
14982                 this.data[n] = m[n];
14983             }
14984         }
14985         this.dirty = false;
14986         delete this.modified;
14987         this.editing = false;
14988         if(this.store){
14989             this.store.afterReject(this);
14990         }
14991     },
14992
14993     /**
14994      * Usually called by the {@link Roo.data.Store} which owns the Record.
14995      * Commits all changes made to the Record since either creation, or the last commit operation.
14996      * <p>
14997      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14998      * of commit operations.
14999      */
15000     commit : function(){
15001         this.dirty = false;
15002         delete this.modified;
15003         this.editing = false;
15004         if(this.store){
15005             this.store.afterCommit(this);
15006         }
15007     },
15008
15009     // private
15010     hasError : function(){
15011         return this.error != null;
15012     },
15013
15014     // private
15015     clearError : function(){
15016         this.error = null;
15017     },
15018
15019     /**
15020      * Creates a copy of this record.
15021      * @param {String} id (optional) A new record id if you don't want to use this record's id
15022      * @return {Record}
15023      */
15024     copy : function(newId) {
15025         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15026     }
15027 };/*
15028  * Based on:
15029  * Ext JS Library 1.1.1
15030  * Copyright(c) 2006-2007, Ext JS, LLC.
15031  *
15032  * Originally Released Under LGPL - original licence link has changed is not relivant.
15033  *
15034  * Fork - LGPL
15035  * <script type="text/javascript">
15036  */
15037
15038
15039
15040 /**
15041  * @class Roo.data.Store
15042  * @extends Roo.util.Observable
15043  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15044  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15045  * <p>
15046  * 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
15047  * has no knowledge of the format of the data returned by the Proxy.<br>
15048  * <p>
15049  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15050  * instances from the data object. These records are cached and made available through accessor functions.
15051  * @constructor
15052  * Creates a new Store.
15053  * @param {Object} config A config object containing the objects needed for the Store to access data,
15054  * and read the data into Records.
15055  */
15056 Roo.data.Store = function(config){
15057     this.data = new Roo.util.MixedCollection(false);
15058     this.data.getKey = function(o){
15059         return o.id;
15060     };
15061     this.baseParams = {};
15062     // private
15063     this.paramNames = {
15064         "start" : "start",
15065         "limit" : "limit",
15066         "sort" : "sort",
15067         "dir" : "dir",
15068         "multisort" : "_multisort"
15069     };
15070
15071     if(config && config.data){
15072         this.inlineData = config.data;
15073         delete config.data;
15074     }
15075
15076     Roo.apply(this, config);
15077     
15078     if(this.reader){ // reader passed
15079         this.reader = Roo.factory(this.reader, Roo.data);
15080         this.reader.xmodule = this.xmodule || false;
15081         if(!this.recordType){
15082             this.recordType = this.reader.recordType;
15083         }
15084         if(this.reader.onMetaChange){
15085             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15086         }
15087     }
15088
15089     if(this.recordType){
15090         this.fields = this.recordType.prototype.fields;
15091     }
15092     this.modified = [];
15093
15094     this.addEvents({
15095         /**
15096          * @event datachanged
15097          * Fires when the data cache has changed, and a widget which is using this Store
15098          * as a Record cache should refresh its view.
15099          * @param {Store} this
15100          */
15101         datachanged : true,
15102         /**
15103          * @event metachange
15104          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15105          * @param {Store} this
15106          * @param {Object} meta The JSON metadata
15107          */
15108         metachange : true,
15109         /**
15110          * @event add
15111          * Fires when Records have been added to the Store
15112          * @param {Store} this
15113          * @param {Roo.data.Record[]} records The array of Records added
15114          * @param {Number} index The index at which the record(s) were added
15115          */
15116         add : true,
15117         /**
15118          * @event remove
15119          * Fires when a Record has been removed from the Store
15120          * @param {Store} this
15121          * @param {Roo.data.Record} record The Record that was removed
15122          * @param {Number} index The index at which the record was removed
15123          */
15124         remove : true,
15125         /**
15126          * @event update
15127          * Fires when a Record has been updated
15128          * @param {Store} this
15129          * @param {Roo.data.Record} record The Record that was updated
15130          * @param {String} operation The update operation being performed.  Value may be one of:
15131          * <pre><code>
15132  Roo.data.Record.EDIT
15133  Roo.data.Record.REJECT
15134  Roo.data.Record.COMMIT
15135          * </code></pre>
15136          */
15137         update : true,
15138         /**
15139          * @event clear
15140          * Fires when the data cache has been cleared.
15141          * @param {Store} this
15142          */
15143         clear : true,
15144         /**
15145          * @event beforeload
15146          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15147          * the load action will be canceled.
15148          * @param {Store} this
15149          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15150          */
15151         beforeload : true,
15152         /**
15153          * @event beforeloadadd
15154          * Fires after a new set of Records has been loaded.
15155          * @param {Store} this
15156          * @param {Roo.data.Record[]} records The Records that were loaded
15157          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15158          */
15159         beforeloadadd : true,
15160         /**
15161          * @event load
15162          * Fires after a new set of Records has been loaded, before they are added to the store.
15163          * @param {Store} this
15164          * @param {Roo.data.Record[]} records The Records that were loaded
15165          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15166          * @params {Object} return from reader
15167          */
15168         load : true,
15169         /**
15170          * @event loadexception
15171          * Fires if an exception occurs in the Proxy during loading.
15172          * Called with the signature of the Proxy's "loadexception" event.
15173          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15174          * 
15175          * @param {Proxy} 
15176          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15177          * @param {Object} load options 
15178          * @param {Object} jsonData from your request (normally this contains the Exception)
15179          */
15180         loadexception : true
15181     });
15182     
15183     if(this.proxy){
15184         this.proxy = Roo.factory(this.proxy, Roo.data);
15185         this.proxy.xmodule = this.xmodule || false;
15186         this.relayEvents(this.proxy,  ["loadexception"]);
15187     }
15188     this.sortToggle = {};
15189     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15190
15191     Roo.data.Store.superclass.constructor.call(this);
15192
15193     if(this.inlineData){
15194         this.loadData(this.inlineData);
15195         delete this.inlineData;
15196     }
15197 };
15198
15199 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15200      /**
15201     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15202     * without a remote query - used by combo/forms at present.
15203     */
15204     
15205     /**
15206     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15207     */
15208     /**
15209     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15210     */
15211     /**
15212     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15213     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15214     */
15215     /**
15216     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15217     * on any HTTP request
15218     */
15219     /**
15220     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15221     */
15222     /**
15223     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15224     */
15225     multiSort: false,
15226     /**
15227     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15228     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15229     */
15230     remoteSort : false,
15231
15232     /**
15233     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15234      * loaded or when a record is removed. (defaults to false).
15235     */
15236     pruneModifiedRecords : false,
15237
15238     // private
15239     lastOptions : null,
15240
15241     /**
15242      * Add Records to the Store and fires the add event.
15243      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15244      */
15245     add : function(records){
15246         records = [].concat(records);
15247         for(var i = 0, len = records.length; i < len; i++){
15248             records[i].join(this);
15249         }
15250         var index = this.data.length;
15251         this.data.addAll(records);
15252         this.fireEvent("add", this, records, index);
15253     },
15254
15255     /**
15256      * Remove a Record from the Store and fires the remove event.
15257      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15258      */
15259     remove : function(record){
15260         var index = this.data.indexOf(record);
15261         this.data.removeAt(index);
15262  
15263         if(this.pruneModifiedRecords){
15264             this.modified.remove(record);
15265         }
15266         this.fireEvent("remove", this, record, index);
15267     },
15268
15269     /**
15270      * Remove all Records from the Store and fires the clear event.
15271      */
15272     removeAll : function(){
15273         this.data.clear();
15274         if(this.pruneModifiedRecords){
15275             this.modified = [];
15276         }
15277         this.fireEvent("clear", this);
15278     },
15279
15280     /**
15281      * Inserts Records to the Store at the given index and fires the add event.
15282      * @param {Number} index The start index at which to insert the passed Records.
15283      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15284      */
15285     insert : function(index, records){
15286         records = [].concat(records);
15287         for(var i = 0, len = records.length; i < len; i++){
15288             this.data.insert(index, records[i]);
15289             records[i].join(this);
15290         }
15291         this.fireEvent("add", this, records, index);
15292     },
15293
15294     /**
15295      * Get the index within the cache of the passed Record.
15296      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15297      * @return {Number} The index of the passed Record. Returns -1 if not found.
15298      */
15299     indexOf : function(record){
15300         return this.data.indexOf(record);
15301     },
15302
15303     /**
15304      * Get the index within the cache of the Record with the passed id.
15305      * @param {String} id The id of the Record to find.
15306      * @return {Number} The index of the Record. Returns -1 if not found.
15307      */
15308     indexOfId : function(id){
15309         return this.data.indexOfKey(id);
15310     },
15311
15312     /**
15313      * Get the Record with the specified id.
15314      * @param {String} id The id of the Record to find.
15315      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15316      */
15317     getById : function(id){
15318         return this.data.key(id);
15319     },
15320
15321     /**
15322      * Get the Record at the specified index.
15323      * @param {Number} index The index of the Record to find.
15324      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15325      */
15326     getAt : function(index){
15327         return this.data.itemAt(index);
15328     },
15329
15330     /**
15331      * Returns a range of Records between specified indices.
15332      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15333      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15334      * @return {Roo.data.Record[]} An array of Records
15335      */
15336     getRange : function(start, end){
15337         return this.data.getRange(start, end);
15338     },
15339
15340     // private
15341     storeOptions : function(o){
15342         o = Roo.apply({}, o);
15343         delete o.callback;
15344         delete o.scope;
15345         this.lastOptions = o;
15346     },
15347
15348     /**
15349      * Loads the Record cache from the configured Proxy using the configured Reader.
15350      * <p>
15351      * If using remote paging, then the first load call must specify the <em>start</em>
15352      * and <em>limit</em> properties in the options.params property to establish the initial
15353      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15354      * <p>
15355      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15356      * and this call will return before the new data has been loaded. Perform any post-processing
15357      * in a callback function, or in a "load" event handler.</strong>
15358      * <p>
15359      * @param {Object} options An object containing properties which control loading options:<ul>
15360      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15361      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15362      * <pre>
15363                 {
15364                     data : data,  // array of key=>value data like JsonReader
15365                     total : data.length,
15366                     success : true
15367                     
15368                 }
15369         </pre>
15370             }.</li>
15371      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15372      * passed the following arguments:<ul>
15373      * <li>r : Roo.data.Record[]</li>
15374      * <li>options: Options object from the load call</li>
15375      * <li>success: Boolean success indicator</li></ul></li>
15376      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15377      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15378      * </ul>
15379      */
15380     load : function(options){
15381         options = options || {};
15382         if(this.fireEvent("beforeload", this, options) !== false){
15383             this.storeOptions(options);
15384             var p = Roo.apply(options.params || {}, this.baseParams);
15385             // if meta was not loaded from remote source.. try requesting it.
15386             if (!this.reader.metaFromRemote) {
15387                 p._requestMeta = 1;
15388             }
15389             if(this.sortInfo && this.remoteSort){
15390                 var pn = this.paramNames;
15391                 p[pn["sort"]] = this.sortInfo.field;
15392                 p[pn["dir"]] = this.sortInfo.direction;
15393             }
15394             if (this.multiSort) {
15395                 var pn = this.paramNames;
15396                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15397             }
15398             
15399             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15400         }
15401     },
15402
15403     /**
15404      * Reloads the Record cache from the configured Proxy using the configured Reader and
15405      * the options from the last load operation performed.
15406      * @param {Object} options (optional) An object containing properties which may override the options
15407      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15408      * the most recently used options are reused).
15409      */
15410     reload : function(options){
15411         this.load(Roo.applyIf(options||{}, this.lastOptions));
15412     },
15413
15414     // private
15415     // Called as a callback by the Reader during a load operation.
15416     loadRecords : function(o, options, success){
15417          
15418         if(!o){
15419             if(success !== false){
15420                 this.fireEvent("load", this, [], options, o);
15421             }
15422             if(options.callback){
15423                 options.callback.call(options.scope || this, [], options, false);
15424             }
15425             return;
15426         }
15427         // if data returned failure - throw an exception.
15428         if (o.success === false) {
15429             // show a message if no listener is registered.
15430             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15431                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15432             }
15433             // loadmask wil be hooked into this..
15434             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15435             return;
15436         }
15437         var r = o.records, t = o.totalRecords || r.length;
15438         
15439         this.fireEvent("beforeloadadd", this, r, options, o);
15440         
15441         if(!options || options.add !== true){
15442             if(this.pruneModifiedRecords){
15443                 this.modified = [];
15444             }
15445             for(var i = 0, len = r.length; i < len; i++){
15446                 r[i].join(this);
15447             }
15448             if(this.snapshot){
15449                 this.data = this.snapshot;
15450                 delete this.snapshot;
15451             }
15452             this.data.clear();
15453             this.data.addAll(r);
15454             this.totalLength = t;
15455             this.applySort();
15456             this.fireEvent("datachanged", this);
15457         }else{
15458             this.totalLength = Math.max(t, this.data.length+r.length);
15459             this.add(r);
15460         }
15461         
15462         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15463                 
15464             var e = new Roo.data.Record({});
15465
15466             e.set(this.parent.displayField, this.parent.emptyTitle);
15467             e.set(this.parent.valueField, '');
15468
15469             this.insert(0, e);
15470         }
15471             
15472         this.fireEvent("load", this, r, options, o);
15473         if(options.callback){
15474             options.callback.call(options.scope || this, r, options, true);
15475         }
15476     },
15477
15478
15479     /**
15480      * Loads data from a passed data block. A Reader which understands the format of the data
15481      * must have been configured in the constructor.
15482      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15483      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15484      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15485      */
15486     loadData : function(o, append){
15487         var r = this.reader.readRecords(o);
15488         this.loadRecords(r, {add: append}, true);
15489     },
15490     
15491      /**
15492      * using 'cn' the nested child reader read the child array into it's child stores.
15493      * @param {Object} rec The record with a 'children array
15494      */
15495     loadDataFromChildren : function(rec)
15496     {
15497         this.loadData(this.reader.toLoadData(rec));
15498     },
15499     
15500
15501     /**
15502      * Gets the number of cached records.
15503      * <p>
15504      * <em>If using paging, this may not be the total size of the dataset. If the data object
15505      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15506      * the data set size</em>
15507      */
15508     getCount : function(){
15509         return this.data.length || 0;
15510     },
15511
15512     /**
15513      * Gets the total number of records in the dataset as returned by the server.
15514      * <p>
15515      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15516      * the dataset size</em>
15517      */
15518     getTotalCount : function(){
15519         return this.totalLength || 0;
15520     },
15521
15522     /**
15523      * Returns the sort state of the Store as an object with two properties:
15524      * <pre><code>
15525  field {String} The name of the field by which the Records are sorted
15526  direction {String} The sort order, "ASC" or "DESC"
15527      * </code></pre>
15528      */
15529     getSortState : function(){
15530         return this.sortInfo;
15531     },
15532
15533     // private
15534     applySort : function(){
15535         if(this.sortInfo && !this.remoteSort){
15536             var s = this.sortInfo, f = s.field;
15537             var st = this.fields.get(f).sortType;
15538             var fn = function(r1, r2){
15539                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15540                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15541             };
15542             this.data.sort(s.direction, fn);
15543             if(this.snapshot && this.snapshot != this.data){
15544                 this.snapshot.sort(s.direction, fn);
15545             }
15546         }
15547     },
15548
15549     /**
15550      * Sets the default sort column and order to be used by the next load operation.
15551      * @param {String} fieldName The name of the field to sort by.
15552      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15553      */
15554     setDefaultSort : function(field, dir){
15555         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15556     },
15557
15558     /**
15559      * Sort the Records.
15560      * If remote sorting is used, the sort is performed on the server, and the cache is
15561      * reloaded. If local sorting is used, the cache is sorted internally.
15562      * @param {String} fieldName The name of the field to sort by.
15563      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15564      */
15565     sort : function(fieldName, dir){
15566         var f = this.fields.get(fieldName);
15567         if(!dir){
15568             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15569             
15570             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15571                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15572             }else{
15573                 dir = f.sortDir;
15574             }
15575         }
15576         this.sortToggle[f.name] = dir;
15577         this.sortInfo = {field: f.name, direction: dir};
15578         if(!this.remoteSort){
15579             this.applySort();
15580             this.fireEvent("datachanged", this);
15581         }else{
15582             this.load(this.lastOptions);
15583         }
15584     },
15585
15586     /**
15587      * Calls the specified function for each of the Records in the cache.
15588      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15589      * Returning <em>false</em> aborts and exits the iteration.
15590      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15591      */
15592     each : function(fn, scope){
15593         this.data.each(fn, scope);
15594     },
15595
15596     /**
15597      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15598      * (e.g., during paging).
15599      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15600      */
15601     getModifiedRecords : function(){
15602         return this.modified;
15603     },
15604
15605     // private
15606     createFilterFn : function(property, value, anyMatch){
15607         if(!value.exec){ // not a regex
15608             value = String(value);
15609             if(value.length == 0){
15610                 return false;
15611             }
15612             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15613         }
15614         return function(r){
15615             return value.test(r.data[property]);
15616         };
15617     },
15618
15619     /**
15620      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15621      * @param {String} property A field on your records
15622      * @param {Number} start The record index to start at (defaults to 0)
15623      * @param {Number} end The last record index to include (defaults to length - 1)
15624      * @return {Number} The sum
15625      */
15626     sum : function(property, start, end){
15627         var rs = this.data.items, v = 0;
15628         start = start || 0;
15629         end = (end || end === 0) ? end : rs.length-1;
15630
15631         for(var i = start; i <= end; i++){
15632             v += (rs[i].data[property] || 0);
15633         }
15634         return v;
15635     },
15636
15637     /**
15638      * Filter the records by a specified property.
15639      * @param {String} field A field on your records
15640      * @param {String/RegExp} value Either a string that the field
15641      * should start with or a RegExp to test against the field
15642      * @param {Boolean} anyMatch True to match any part not just the beginning
15643      */
15644     filter : function(property, value, anyMatch){
15645         var fn = this.createFilterFn(property, value, anyMatch);
15646         return fn ? this.filterBy(fn) : this.clearFilter();
15647     },
15648
15649     /**
15650      * Filter by a function. The specified function will be called with each
15651      * record in this data source. If the function returns true the record is included,
15652      * otherwise it is filtered.
15653      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15654      * @param {Object} scope (optional) The scope of the function (defaults to this)
15655      */
15656     filterBy : function(fn, scope){
15657         this.snapshot = this.snapshot || this.data;
15658         this.data = this.queryBy(fn, scope||this);
15659         this.fireEvent("datachanged", this);
15660     },
15661
15662     /**
15663      * Query the records by a specified property.
15664      * @param {String} field A field on your records
15665      * @param {String/RegExp} value Either a string that the field
15666      * should start with or a RegExp to test against the field
15667      * @param {Boolean} anyMatch True to match any part not just the beginning
15668      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15669      */
15670     query : function(property, value, anyMatch){
15671         var fn = this.createFilterFn(property, value, anyMatch);
15672         return fn ? this.queryBy(fn) : this.data.clone();
15673     },
15674
15675     /**
15676      * Query by a function. The specified function will be called with each
15677      * record in this data source. If the function returns true the record is included
15678      * in the results.
15679      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15680      * @param {Object} scope (optional) The scope of the function (defaults to this)
15681       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15682      **/
15683     queryBy : function(fn, scope){
15684         var data = this.snapshot || this.data;
15685         return data.filterBy(fn, scope||this);
15686     },
15687
15688     /**
15689      * Collects unique values for a particular dataIndex from this store.
15690      * @param {String} dataIndex The property to collect
15691      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15692      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15693      * @return {Array} An array of the unique values
15694      **/
15695     collect : function(dataIndex, allowNull, bypassFilter){
15696         var d = (bypassFilter === true && this.snapshot) ?
15697                 this.snapshot.items : this.data.items;
15698         var v, sv, r = [], l = {};
15699         for(var i = 0, len = d.length; i < len; i++){
15700             v = d[i].data[dataIndex];
15701             sv = String(v);
15702             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15703                 l[sv] = true;
15704                 r[r.length] = v;
15705             }
15706         }
15707         return r;
15708     },
15709
15710     /**
15711      * Revert to a view of the Record cache with no filtering applied.
15712      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15713      */
15714     clearFilter : function(suppressEvent){
15715         if(this.snapshot && this.snapshot != this.data){
15716             this.data = this.snapshot;
15717             delete this.snapshot;
15718             if(suppressEvent !== true){
15719                 this.fireEvent("datachanged", this);
15720             }
15721         }
15722     },
15723
15724     // private
15725     afterEdit : function(record){
15726         if(this.modified.indexOf(record) == -1){
15727             this.modified.push(record);
15728         }
15729         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15730     },
15731     
15732     // private
15733     afterReject : function(record){
15734         this.modified.remove(record);
15735         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15736     },
15737
15738     // private
15739     afterCommit : function(record){
15740         this.modified.remove(record);
15741         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15742     },
15743
15744     /**
15745      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15746      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15747      */
15748     commitChanges : function(){
15749         var m = this.modified.slice(0);
15750         this.modified = [];
15751         for(var i = 0, len = m.length; i < len; i++){
15752             m[i].commit();
15753         }
15754     },
15755
15756     /**
15757      * Cancel outstanding changes on all changed records.
15758      */
15759     rejectChanges : function(){
15760         var m = this.modified.slice(0);
15761         this.modified = [];
15762         for(var i = 0, len = m.length; i < len; i++){
15763             m[i].reject();
15764         }
15765     },
15766
15767     onMetaChange : function(meta, rtype, o){
15768         this.recordType = rtype;
15769         this.fields = rtype.prototype.fields;
15770         delete this.snapshot;
15771         this.sortInfo = meta.sortInfo || this.sortInfo;
15772         this.modified = [];
15773         this.fireEvent('metachange', this, this.reader.meta);
15774     },
15775     
15776     moveIndex : function(data, type)
15777     {
15778         var index = this.indexOf(data);
15779         
15780         var newIndex = index + type;
15781         
15782         this.remove(data);
15783         
15784         this.insert(newIndex, data);
15785         
15786     }
15787 });/*
15788  * Based on:
15789  * Ext JS Library 1.1.1
15790  * Copyright(c) 2006-2007, Ext JS, LLC.
15791  *
15792  * Originally Released Under LGPL - original licence link has changed is not relivant.
15793  *
15794  * Fork - LGPL
15795  * <script type="text/javascript">
15796  */
15797
15798 /**
15799  * @class Roo.data.SimpleStore
15800  * @extends Roo.data.Store
15801  * Small helper class to make creating Stores from Array data easier.
15802  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15803  * @cfg {Array} fields An array of field definition objects, or field name strings.
15804  * @cfg {Object} an existing reader (eg. copied from another store)
15805  * @cfg {Array} data The multi-dimensional array of data
15806  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15807  * @cfg {Roo.data.Reader} reader  [not-required] 
15808  * @constructor
15809  * @param {Object} config
15810  */
15811 Roo.data.SimpleStore = function(config)
15812 {
15813     Roo.data.SimpleStore.superclass.constructor.call(this, {
15814         isLocal : true,
15815         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15816                 id: config.id
15817             },
15818             Roo.data.Record.create(config.fields)
15819         ),
15820         proxy : new Roo.data.MemoryProxy(config.data)
15821     });
15822     this.load();
15823 };
15824 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15825  * Based on:
15826  * Ext JS Library 1.1.1
15827  * Copyright(c) 2006-2007, Ext JS, LLC.
15828  *
15829  * Originally Released Under LGPL - original licence link has changed is not relivant.
15830  *
15831  * Fork - LGPL
15832  * <script type="text/javascript">
15833  */
15834
15835 /**
15836 /**
15837  * @extends Roo.data.Store
15838  * @class Roo.data.JsonStore
15839  * Small helper class to make creating Stores for JSON data easier. <br/>
15840 <pre><code>
15841 var store = new Roo.data.JsonStore({
15842     url: 'get-images.php',
15843     root: 'images',
15844     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15845 });
15846 </code></pre>
15847  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15848  * JsonReader and HttpProxy (unless inline data is provided).</b>
15849  * @cfg {Array} fields An array of field definition objects, or field name strings.
15850  * @constructor
15851  * @param {Object} config
15852  */
15853 Roo.data.JsonStore = function(c){
15854     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15855         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15856         reader: new Roo.data.JsonReader(c, c.fields)
15857     }));
15858 };
15859 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15860  * Based on:
15861  * Ext JS Library 1.1.1
15862  * Copyright(c) 2006-2007, Ext JS, LLC.
15863  *
15864  * Originally Released Under LGPL - original licence link has changed is not relivant.
15865  *
15866  * Fork - LGPL
15867  * <script type="text/javascript">
15868  */
15869
15870  
15871 Roo.data.Field = function(config){
15872     if(typeof config == "string"){
15873         config = {name: config};
15874     }
15875     Roo.apply(this, config);
15876     
15877     if(!this.type){
15878         this.type = "auto";
15879     }
15880     
15881     var st = Roo.data.SortTypes;
15882     // named sortTypes are supported, here we look them up
15883     if(typeof this.sortType == "string"){
15884         this.sortType = st[this.sortType];
15885     }
15886     
15887     // set default sortType for strings and dates
15888     if(!this.sortType){
15889         switch(this.type){
15890             case "string":
15891                 this.sortType = st.asUCString;
15892                 break;
15893             case "date":
15894                 this.sortType = st.asDate;
15895                 break;
15896             default:
15897                 this.sortType = st.none;
15898         }
15899     }
15900
15901     // define once
15902     var stripRe = /[\$,%]/g;
15903
15904     // prebuilt conversion function for this field, instead of
15905     // switching every time we're reading a value
15906     if(!this.convert){
15907         var cv, dateFormat = this.dateFormat;
15908         switch(this.type){
15909             case "":
15910             case "auto":
15911             case undefined:
15912                 cv = function(v){ return v; };
15913                 break;
15914             case "string":
15915                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15916                 break;
15917             case "int":
15918                 cv = function(v){
15919                     return v !== undefined && v !== null && v !== '' ?
15920                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15921                     };
15922                 break;
15923             case "float":
15924                 cv = function(v){
15925                     return v !== undefined && v !== null && v !== '' ?
15926                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15927                     };
15928                 break;
15929             case "bool":
15930             case "boolean":
15931                 cv = function(v){ return v === true || v === "true" || v == 1; };
15932                 break;
15933             case "date":
15934                 cv = function(v){
15935                     if(!v){
15936                         return '';
15937                     }
15938                     if(v instanceof Date){
15939                         return v;
15940                     }
15941                     if(dateFormat){
15942                         if(dateFormat == "timestamp"){
15943                             return new Date(v*1000);
15944                         }
15945                         return Date.parseDate(v, dateFormat);
15946                     }
15947                     var parsed = Date.parse(v);
15948                     return parsed ? new Date(parsed) : null;
15949                 };
15950              break;
15951             
15952         }
15953         this.convert = cv;
15954     }
15955 };
15956
15957 Roo.data.Field.prototype = {
15958     dateFormat: null,
15959     defaultValue: "",
15960     mapping: null,
15961     sortType : null,
15962     sortDir : "ASC"
15963 };/*
15964  * Based on:
15965  * Ext JS Library 1.1.1
15966  * Copyright(c) 2006-2007, Ext JS, LLC.
15967  *
15968  * Originally Released Under LGPL - original licence link has changed is not relivant.
15969  *
15970  * Fork - LGPL
15971  * <script type="text/javascript">
15972  */
15973  
15974 // Base class for reading structured data from a data source.  This class is intended to be
15975 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15976
15977 /**
15978  * @class Roo.data.DataReader
15979  * @abstract
15980  * Base class for reading structured data from a data source.  This class is intended to be
15981  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15982  */
15983
15984 Roo.data.DataReader = function(meta, recordType){
15985     
15986     this.meta = meta;
15987     
15988     this.recordType = recordType instanceof Array ? 
15989         Roo.data.Record.create(recordType) : recordType;
15990 };
15991
15992 Roo.data.DataReader.prototype = {
15993     
15994     
15995     readerType : 'Data',
15996      /**
15997      * Create an empty record
15998      * @param {Object} data (optional) - overlay some values
15999      * @return {Roo.data.Record} record created.
16000      */
16001     newRow :  function(d) {
16002         var da =  {};
16003         this.recordType.prototype.fields.each(function(c) {
16004             switch( c.type) {
16005                 case 'int' : da[c.name] = 0; break;
16006                 case 'date' : da[c.name] = new Date(); break;
16007                 case 'float' : da[c.name] = 0.0; break;
16008                 case 'boolean' : da[c.name] = false; break;
16009                 default : da[c.name] = ""; break;
16010             }
16011             
16012         });
16013         return new this.recordType(Roo.apply(da, d));
16014     }
16015     
16016     
16017 };/*
16018  * Based on:
16019  * Ext JS Library 1.1.1
16020  * Copyright(c) 2006-2007, Ext JS, LLC.
16021  *
16022  * Originally Released Under LGPL - original licence link has changed is not relivant.
16023  *
16024  * Fork - LGPL
16025  * <script type="text/javascript">
16026  */
16027
16028 /**
16029  * @class Roo.data.DataProxy
16030  * @extends Roo.util.Observable
16031  * @abstract
16032  * This class is an abstract base class for implementations which provide retrieval of
16033  * unformatted data objects.<br>
16034  * <p>
16035  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16036  * (of the appropriate type which knows how to parse the data object) to provide a block of
16037  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16038  * <p>
16039  * Custom implementations must implement the load method as described in
16040  * {@link Roo.data.HttpProxy#load}.
16041  */
16042 Roo.data.DataProxy = function(){
16043     this.addEvents({
16044         /**
16045          * @event beforeload
16046          * Fires before a network request is made to retrieve a data object.
16047          * @param {Object} This DataProxy object.
16048          * @param {Object} params The params parameter to the load function.
16049          */
16050         beforeload : true,
16051         /**
16052          * @event load
16053          * Fires before the load method's callback is called.
16054          * @param {Object} This DataProxy object.
16055          * @param {Object} o The data object.
16056          * @param {Object} arg The callback argument object passed to the load function.
16057          */
16058         load : true,
16059         /**
16060          * @event loadexception
16061          * Fires if an Exception occurs during data retrieval.
16062          * @param {Object} This DataProxy object.
16063          * @param {Object} o The data object.
16064          * @param {Object} arg The callback argument object passed to the load function.
16065          * @param {Object} e The Exception.
16066          */
16067         loadexception : true
16068     });
16069     Roo.data.DataProxy.superclass.constructor.call(this);
16070 };
16071
16072 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16073
16074     /**
16075      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16076      */
16077 /*
16078  * Based on:
16079  * Ext JS Library 1.1.1
16080  * Copyright(c) 2006-2007, Ext JS, LLC.
16081  *
16082  * Originally Released Under LGPL - original licence link has changed is not relivant.
16083  *
16084  * Fork - LGPL
16085  * <script type="text/javascript">
16086  */
16087 /**
16088  * @class Roo.data.MemoryProxy
16089  * @extends Roo.data.DataProxy
16090  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16091  * to the Reader when its load method is called.
16092  * @constructor
16093  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16094  */
16095 Roo.data.MemoryProxy = function(config){
16096     var data = config;
16097     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16098         data = config.data;
16099     }
16100     Roo.data.MemoryProxy.superclass.constructor.call(this);
16101     this.data = data;
16102 };
16103
16104 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16105     
16106     /**
16107      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16108      */
16109     /**
16110      * Load data from the requested source (in this case an in-memory
16111      * data object passed to the constructor), read the data object into
16112      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16113      * process that block using the passed callback.
16114      * @param {Object} params This parameter is not used by the MemoryProxy class.
16115      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16116      * object into a block of Roo.data.Records.
16117      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16118      * The function must be passed <ul>
16119      * <li>The Record block object</li>
16120      * <li>The "arg" argument from the load function</li>
16121      * <li>A boolean success indicator</li>
16122      * </ul>
16123      * @param {Object} scope The scope in which to call the callback
16124      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16125      */
16126     load : function(params, reader, callback, scope, arg){
16127         params = params || {};
16128         var result;
16129         try {
16130             result = reader.readRecords(params.data ? params.data :this.data);
16131         }catch(e){
16132             this.fireEvent("loadexception", this, arg, null, e);
16133             callback.call(scope, null, arg, false);
16134             return;
16135         }
16136         callback.call(scope, result, arg, true);
16137     },
16138     
16139     // private
16140     update : function(params, records){
16141         
16142     }
16143 });/*
16144  * Based on:
16145  * Ext JS Library 1.1.1
16146  * Copyright(c) 2006-2007, Ext JS, LLC.
16147  *
16148  * Originally Released Under LGPL - original licence link has changed is not relivant.
16149  *
16150  * Fork - LGPL
16151  * <script type="text/javascript">
16152  */
16153 /**
16154  * @class Roo.data.HttpProxy
16155  * @extends Roo.data.DataProxy
16156  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16157  * configured to reference a certain URL.<br><br>
16158  * <p>
16159  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16160  * from which the running page was served.<br><br>
16161  * <p>
16162  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16163  * <p>
16164  * Be aware that to enable the browser to parse an XML document, the server must set
16165  * the Content-Type header in the HTTP response to "text/xml".
16166  * @constructor
16167  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16168  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16169  * will be used to make the request.
16170  */
16171 Roo.data.HttpProxy = function(conn){
16172     Roo.data.HttpProxy.superclass.constructor.call(this);
16173     // is conn a conn config or a real conn?
16174     this.conn = conn;
16175     this.useAjax = !conn || !conn.events;
16176   
16177 };
16178
16179 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16180     // thse are take from connection...
16181     
16182     /**
16183      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16184      */
16185     /**
16186      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16187      * extra parameters to each request made by this object. (defaults to undefined)
16188      */
16189     /**
16190      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16191      *  to each request made by this object. (defaults to undefined)
16192      */
16193     /**
16194      * @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)
16195      */
16196     /**
16197      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16198      */
16199      /**
16200      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16201      * @type Boolean
16202      */
16203   
16204
16205     /**
16206      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16207      * @type Boolean
16208      */
16209     /**
16210      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16211      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16212      * a finer-grained basis than the DataProxy events.
16213      */
16214     getConnection : function(){
16215         return this.useAjax ? Roo.Ajax : this.conn;
16216     },
16217
16218     /**
16219      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16220      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16221      * process that block using the passed callback.
16222      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16223      * for the request to the remote server.
16224      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16225      * object into a block of Roo.data.Records.
16226      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16227      * The function must be passed <ul>
16228      * <li>The Record block object</li>
16229      * <li>The "arg" argument from the load function</li>
16230      * <li>A boolean success indicator</li>
16231      * </ul>
16232      * @param {Object} scope The scope in which to call the callback
16233      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16234      */
16235     load : function(params, reader, callback, scope, arg){
16236         if(this.fireEvent("beforeload", this, params) !== false){
16237             var  o = {
16238                 params : params || {},
16239                 request: {
16240                     callback : callback,
16241                     scope : scope,
16242                     arg : arg
16243                 },
16244                 reader: reader,
16245                 callback : this.loadResponse,
16246                 scope: this
16247             };
16248             if(this.useAjax){
16249                 Roo.applyIf(o, this.conn);
16250                 if(this.activeRequest){
16251                     Roo.Ajax.abort(this.activeRequest);
16252                 }
16253                 this.activeRequest = Roo.Ajax.request(o);
16254             }else{
16255                 this.conn.request(o);
16256             }
16257         }else{
16258             callback.call(scope||this, null, arg, false);
16259         }
16260     },
16261
16262     // private
16263     loadResponse : function(o, success, response){
16264         delete this.activeRequest;
16265         if(!success){
16266             this.fireEvent("loadexception", this, o, response);
16267             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16268             return;
16269         }
16270         var result;
16271         try {
16272             result = o.reader.read(response);
16273         }catch(e){
16274             o.success = false;
16275             o.raw = { errorMsg : response.responseText };
16276             this.fireEvent("loadexception", this, o, response, e);
16277             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16278             return;
16279         }
16280         
16281         this.fireEvent("load", this, o, o.request.arg);
16282         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16283     },
16284
16285     // private
16286     update : function(dataSet){
16287
16288     },
16289
16290     // private
16291     updateResponse : function(dataSet){
16292
16293     }
16294 });/*
16295  * Based on:
16296  * Ext JS Library 1.1.1
16297  * Copyright(c) 2006-2007, Ext JS, LLC.
16298  *
16299  * Originally Released Under LGPL - original licence link has changed is not relivant.
16300  *
16301  * Fork - LGPL
16302  * <script type="text/javascript">
16303  */
16304
16305 /**
16306  * @class Roo.data.ScriptTagProxy
16307  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16308  * other than the originating domain of the running page.<br><br>
16309  * <p>
16310  * <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
16311  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16312  * <p>
16313  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16314  * source code that is used as the source inside a &lt;script> tag.<br><br>
16315  * <p>
16316  * In order for the browser to process the returned data, the server must wrap the data object
16317  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16318  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16319  * depending on whether the callback name was passed:
16320  * <p>
16321  * <pre><code>
16322 boolean scriptTag = false;
16323 String cb = request.getParameter("callback");
16324 if (cb != null) {
16325     scriptTag = true;
16326     response.setContentType("text/javascript");
16327 } else {
16328     response.setContentType("application/x-json");
16329 }
16330 Writer out = response.getWriter();
16331 if (scriptTag) {
16332     out.write(cb + "(");
16333 }
16334 out.print(dataBlock.toJsonString());
16335 if (scriptTag) {
16336     out.write(");");
16337 }
16338 </pre></code>
16339  *
16340  * @constructor
16341  * @param {Object} config A configuration object.
16342  */
16343 Roo.data.ScriptTagProxy = function(config){
16344     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16345     Roo.apply(this, config);
16346     this.head = document.getElementsByTagName("head")[0];
16347 };
16348
16349 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16350
16351 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16352     /**
16353      * @cfg {String} url The URL from which to request the data object.
16354      */
16355     /**
16356      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16357      */
16358     timeout : 30000,
16359     /**
16360      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16361      * the server the name of the callback function set up by the load call to process the returned data object.
16362      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16363      * javascript output which calls this named function passing the data object as its only parameter.
16364      */
16365     callbackParam : "callback",
16366     /**
16367      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16368      * name to the request.
16369      */
16370     nocache : true,
16371
16372     /**
16373      * Load data from the configured URL, read the data object into
16374      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16375      * process that block using the passed callback.
16376      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16377      * for the request to the remote server.
16378      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16379      * object into a block of Roo.data.Records.
16380      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16381      * The function must be passed <ul>
16382      * <li>The Record block object</li>
16383      * <li>The "arg" argument from the load function</li>
16384      * <li>A boolean success indicator</li>
16385      * </ul>
16386      * @param {Object} scope The scope in which to call the callback
16387      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16388      */
16389     load : function(params, reader, callback, scope, arg){
16390         if(this.fireEvent("beforeload", this, params) !== false){
16391
16392             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16393
16394             var url = this.url;
16395             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16396             if(this.nocache){
16397                 url += "&_dc=" + (new Date().getTime());
16398             }
16399             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16400             var trans = {
16401                 id : transId,
16402                 cb : "stcCallback"+transId,
16403                 scriptId : "stcScript"+transId,
16404                 params : params,
16405                 arg : arg,
16406                 url : url,
16407                 callback : callback,
16408                 scope : scope,
16409                 reader : reader
16410             };
16411             var conn = this;
16412
16413             window[trans.cb] = function(o){
16414                 conn.handleResponse(o, trans);
16415             };
16416
16417             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16418
16419             if(this.autoAbort !== false){
16420                 this.abort();
16421             }
16422
16423             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16424
16425             var script = document.createElement("script");
16426             script.setAttribute("src", url);
16427             script.setAttribute("type", "text/javascript");
16428             script.setAttribute("id", trans.scriptId);
16429             this.head.appendChild(script);
16430
16431             this.trans = trans;
16432         }else{
16433             callback.call(scope||this, null, arg, false);
16434         }
16435     },
16436
16437     // private
16438     isLoading : function(){
16439         return this.trans ? true : false;
16440     },
16441
16442     /**
16443      * Abort the current server request.
16444      */
16445     abort : function(){
16446         if(this.isLoading()){
16447             this.destroyTrans(this.trans);
16448         }
16449     },
16450
16451     // private
16452     destroyTrans : function(trans, isLoaded){
16453         this.head.removeChild(document.getElementById(trans.scriptId));
16454         clearTimeout(trans.timeoutId);
16455         if(isLoaded){
16456             window[trans.cb] = undefined;
16457             try{
16458                 delete window[trans.cb];
16459             }catch(e){}
16460         }else{
16461             // if hasn't been loaded, wait for load to remove it to prevent script error
16462             window[trans.cb] = function(){
16463                 window[trans.cb] = undefined;
16464                 try{
16465                     delete window[trans.cb];
16466                 }catch(e){}
16467             };
16468         }
16469     },
16470
16471     // private
16472     handleResponse : function(o, trans){
16473         this.trans = false;
16474         this.destroyTrans(trans, true);
16475         var result;
16476         try {
16477             result = trans.reader.readRecords(o);
16478         }catch(e){
16479             this.fireEvent("loadexception", this, o, trans.arg, e);
16480             trans.callback.call(trans.scope||window, null, trans.arg, false);
16481             return;
16482         }
16483         this.fireEvent("load", this, o, trans.arg);
16484         trans.callback.call(trans.scope||window, result, trans.arg, true);
16485     },
16486
16487     // private
16488     handleFailure : function(trans){
16489         this.trans = false;
16490         this.destroyTrans(trans, false);
16491         this.fireEvent("loadexception", this, null, trans.arg);
16492         trans.callback.call(trans.scope||window, null, trans.arg, false);
16493     }
16494 });/*
16495  * Based on:
16496  * Ext JS Library 1.1.1
16497  * Copyright(c) 2006-2007, Ext JS, LLC.
16498  *
16499  * Originally Released Under LGPL - original licence link has changed is not relivant.
16500  *
16501  * Fork - LGPL
16502  * <script type="text/javascript">
16503  */
16504
16505 /**
16506  * @class Roo.data.JsonReader
16507  * @extends Roo.data.DataReader
16508  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16509  * based on mappings in a provided Roo.data.Record constructor.
16510  * 
16511  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16512  * in the reply previously. 
16513  * 
16514  * <p>
16515  * Example code:
16516  * <pre><code>
16517 var RecordDef = Roo.data.Record.create([
16518     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16519     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16520 ]);
16521 var myReader = new Roo.data.JsonReader({
16522     totalProperty: "results",    // The property which contains the total dataset size (optional)
16523     root: "rows",                // The property which contains an Array of row objects
16524     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16525 }, RecordDef);
16526 </code></pre>
16527  * <p>
16528  * This would consume a JSON file like this:
16529  * <pre><code>
16530 { 'results': 2, 'rows': [
16531     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16532     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16533 }
16534 </code></pre>
16535  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16536  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16537  * paged from the remote server.
16538  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16539  * @cfg {String} root name of the property which contains the Array of row objects.
16540  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16541  * @cfg {Array} fields Array of field definition objects
16542  * @constructor
16543  * Create a new JsonReader
16544  * @param {Object} meta Metadata configuration options
16545  * @param {Object} recordType Either an Array of field definition objects,
16546  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16547  */
16548 Roo.data.JsonReader = function(meta, recordType){
16549     
16550     meta = meta || {};
16551     // set some defaults:
16552     Roo.applyIf(meta, {
16553         totalProperty: 'total',
16554         successProperty : 'success',
16555         root : 'data',
16556         id : 'id'
16557     });
16558     
16559     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16560 };
16561 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16562     
16563     readerType : 'Json',
16564     
16565     /**
16566      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16567      * Used by Store query builder to append _requestMeta to params.
16568      * 
16569      */
16570     metaFromRemote : false,
16571     /**
16572      * This method is only used by a DataProxy which has retrieved data from a remote server.
16573      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16574      * @return {Object} data A data block which is used by an Roo.data.Store object as
16575      * a cache of Roo.data.Records.
16576      */
16577     read : function(response){
16578         var json = response.responseText;
16579        
16580         var o = /* eval:var:o */ eval("("+json+")");
16581         if(!o) {
16582             throw {message: "JsonReader.read: Json object not found"};
16583         }
16584         
16585         if(o.metaData){
16586             
16587             delete this.ef;
16588             this.metaFromRemote = true;
16589             this.meta = o.metaData;
16590             this.recordType = Roo.data.Record.create(o.metaData.fields);
16591             this.onMetaChange(this.meta, this.recordType, o);
16592         }
16593         return this.readRecords(o);
16594     },
16595
16596     // private function a store will implement
16597     onMetaChange : function(meta, recordType, o){
16598
16599     },
16600
16601     /**
16602          * @ignore
16603          */
16604     simpleAccess: function(obj, subsc) {
16605         return obj[subsc];
16606     },
16607
16608         /**
16609          * @ignore
16610          */
16611     getJsonAccessor: function(){
16612         var re = /[\[\.]/;
16613         return function(expr) {
16614             try {
16615                 return(re.test(expr))
16616                     ? new Function("obj", "return obj." + expr)
16617                     : function(obj){
16618                         return obj[expr];
16619                     };
16620             } catch(e){}
16621             return Roo.emptyFn;
16622         };
16623     }(),
16624
16625     /**
16626      * Create a data block containing Roo.data.Records from an XML document.
16627      * @param {Object} o An object which contains an Array of row objects in the property specified
16628      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16629      * which contains the total size of the dataset.
16630      * @return {Object} data A data block which is used by an Roo.data.Store object as
16631      * a cache of Roo.data.Records.
16632      */
16633     readRecords : function(o){
16634         /**
16635          * After any data loads, the raw JSON data is available for further custom processing.
16636          * @type Object
16637          */
16638         this.o = o;
16639         var s = this.meta, Record = this.recordType,
16640             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16641
16642 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16643         if (!this.ef) {
16644             if(s.totalProperty) {
16645                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16646                 }
16647                 if(s.successProperty) {
16648                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16649                 }
16650                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16651                 if (s.id) {
16652                         var g = this.getJsonAccessor(s.id);
16653                         this.getId = function(rec) {
16654                                 var r = g(rec);  
16655                                 return (r === undefined || r === "") ? null : r;
16656                         };
16657                 } else {
16658                         this.getId = function(){return null;};
16659                 }
16660             this.ef = [];
16661             for(var jj = 0; jj < fl; jj++){
16662                 f = fi[jj];
16663                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16664                 this.ef[jj] = this.getJsonAccessor(map);
16665             }
16666         }
16667
16668         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16669         if(s.totalProperty){
16670             var vt = parseInt(this.getTotal(o), 10);
16671             if(!isNaN(vt)){
16672                 totalRecords = vt;
16673             }
16674         }
16675         if(s.successProperty){
16676             var vs = this.getSuccess(o);
16677             if(vs === false || vs === 'false'){
16678                 success = false;
16679             }
16680         }
16681         var records = [];
16682         for(var i = 0; i < c; i++){
16683             var n = root[i];
16684             var values = {};
16685             var id = this.getId(n);
16686             for(var j = 0; j < fl; j++){
16687                 f = fi[j];
16688                                 var v = this.ef[j](n);
16689                                 if (!f.convert) {
16690                                         Roo.log('missing convert for ' + f.name);
16691                                         Roo.log(f);
16692                                         continue;
16693                                 }
16694                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16695             }
16696                         if (!Record) {
16697                                 return {
16698                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16699                                         success : false,
16700                                         records : [],
16701                                         totalRecords : 0
16702                                 };
16703                         }
16704             var record = new Record(values, id);
16705             record.json = n;
16706             records[i] = record;
16707         }
16708         return {
16709             raw : o,
16710             success : success,
16711             records : records,
16712             totalRecords : totalRecords
16713         };
16714     },
16715     // used when loading children.. @see loadDataFromChildren
16716     toLoadData: function(rec)
16717     {
16718         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16719         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16720         return { data : data, total : data.length };
16721         
16722     }
16723 });/*
16724  * Based on:
16725  * Ext JS Library 1.1.1
16726  * Copyright(c) 2006-2007, Ext JS, LLC.
16727  *
16728  * Originally Released Under LGPL - original licence link has changed is not relivant.
16729  *
16730  * Fork - LGPL
16731  * <script type="text/javascript">
16732  */
16733
16734 /**
16735  * @class Roo.data.ArrayReader
16736  * @extends Roo.data.DataReader
16737  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16738  * Each element of that Array represents a row of data fields. The
16739  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16740  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16741  * <p>
16742  * Example code:.
16743  * <pre><code>
16744 var RecordDef = Roo.data.Record.create([
16745     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16746     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16747 ]);
16748 var myReader = new Roo.data.ArrayReader({
16749     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16750 }, RecordDef);
16751 </code></pre>
16752  * <p>
16753  * This would consume an Array like this:
16754  * <pre><code>
16755 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16756   </code></pre>
16757  
16758  * @constructor
16759  * Create a new JsonReader
16760  * @param {Object} meta Metadata configuration options.
16761  * @param {Object|Array} recordType Either an Array of field definition objects
16762  * 
16763  * @cfg {Array} fields Array of field definition objects
16764  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16765  * as specified to {@link Roo.data.Record#create},
16766  * or an {@link Roo.data.Record} object
16767  *
16768  * 
16769  * created using {@link Roo.data.Record#create}.
16770  */
16771 Roo.data.ArrayReader = function(meta, recordType)
16772 {    
16773     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16774 };
16775
16776 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16777     
16778       /**
16779      * Create a data block containing Roo.data.Records from an XML document.
16780      * @param {Object} o An Array of row objects which represents the dataset.
16781      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16782      * a cache of Roo.data.Records.
16783      */
16784     readRecords : function(o)
16785     {
16786         var sid = this.meta ? this.meta.id : null;
16787         var recordType = this.recordType, fields = recordType.prototype.fields;
16788         var records = [];
16789         var root = o;
16790         for(var i = 0; i < root.length; i++){
16791             var n = root[i];
16792             var values = {};
16793             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16794             for(var j = 0, jlen = fields.length; j < jlen; j++){
16795                 var f = fields.items[j];
16796                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16797                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16798                 v = f.convert(v);
16799                 values[f.name] = v;
16800             }
16801             var record = new recordType(values, id);
16802             record.json = n;
16803             records[records.length] = record;
16804         }
16805         return {
16806             records : records,
16807             totalRecords : records.length
16808         };
16809     },
16810     // used when loading children.. @see loadDataFromChildren
16811     toLoadData: function(rec)
16812     {
16813         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16814         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16815         
16816     }
16817     
16818     
16819 });/*
16820  * - LGPL
16821  * * 
16822  */
16823
16824 /**
16825  * @class Roo.bootstrap.form.ComboBox
16826  * @extends Roo.bootstrap.form.TriggerField
16827  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16828  * @cfg {Boolean} append (true|false) default false
16829  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16830  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16831  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16832  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16833  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16834  * @cfg {Boolean} animate default true
16835  * @cfg {Boolean} emptyResultText only for touch device
16836  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16837  * @cfg {String} emptyTitle default ''
16838  * @cfg {Number} width fixed with? experimental
16839  * @constructor
16840  * Create a new ComboBox.
16841  * @param {Object} config Configuration options
16842  */
16843 Roo.bootstrap.form.ComboBox = function(config){
16844     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16845     this.addEvents({
16846         /**
16847          * @event expand
16848          * Fires when the dropdown list is expanded
16849         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16850         */
16851         'expand' : true,
16852         /**
16853          * @event collapse
16854          * Fires when the dropdown list is collapsed
16855         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16856         */
16857         'collapse' : true,
16858         /**
16859          * @event beforeselect
16860          * Fires before a list item is selected. Return false to cancel the selection.
16861         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16862         * @param {Roo.data.Record} record The data record returned from the underlying store
16863         * @param {Number} index The index of the selected item in the dropdown list
16864         */
16865         'beforeselect' : true,
16866         /**
16867          * @event select
16868          * Fires when a list item is selected
16869         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16870         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16871         * @param {Number} index The index of the selected item in the dropdown list
16872         */
16873         'select' : true,
16874         /**
16875          * @event beforequery
16876          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16877          * The event object passed has these properties:
16878         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16879         * @param {String} query The query
16880         * @param {Boolean} forceAll true to force "all" query
16881         * @param {Boolean} cancel true to cancel the query
16882         * @param {Object} e The query event object
16883         */
16884         'beforequery': true,
16885          /**
16886          * @event add
16887          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16888         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16889         */
16890         'add' : true,
16891         /**
16892          * @event edit
16893          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16894         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16895         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16896         */
16897         'edit' : true,
16898         /**
16899          * @event remove
16900          * Fires when the remove value from the combobox array
16901         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16902         */
16903         'remove' : true,
16904         /**
16905          * @event afterremove
16906          * Fires when the remove value from the combobox array
16907         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16908         */
16909         'afterremove' : true,
16910         /**
16911          * @event specialfilter
16912          * Fires when specialfilter
16913             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16914             */
16915         'specialfilter' : true,
16916         /**
16917          * @event tick
16918          * Fires when tick the element
16919             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16920             */
16921         'tick' : true,
16922         /**
16923          * @event touchviewdisplay
16924          * Fires when touch view require special display (default is using displayField)
16925             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16926             * @param {Object} cfg set html .
16927             */
16928         'touchviewdisplay' : true
16929         
16930     });
16931     
16932     this.item = [];
16933     this.tickItems = [];
16934     
16935     this.selectedIndex = -1;
16936     if(this.mode == 'local'){
16937         if(config.queryDelay === undefined){
16938             this.queryDelay = 10;
16939         }
16940         if(config.minChars === undefined){
16941             this.minChars = 0;
16942         }
16943     }
16944 };
16945
16946 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16947      
16948     /**
16949      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16950      * rendering into an Roo.Editor, defaults to false)
16951      */
16952     /**
16953      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16954      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16955      */
16956     /**
16957      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16958      */
16959     /**
16960      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16961      * the dropdown list (defaults to undefined, with no header element)
16962      */
16963
16964      /**
16965      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16966      */
16967      
16968      /**
16969      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16970      */
16971     listWidth: undefined,
16972     /**
16973      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16974      * mode = 'remote' or 'text' if mode = 'local')
16975      */
16976     displayField: undefined,
16977     
16978     /**
16979      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16980      * mode = 'remote' or 'value' if mode = 'local'). 
16981      * Note: use of a valueField requires the user make a selection
16982      * in order for a value to be mapped.
16983      */
16984     valueField: undefined,
16985     /**
16986      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16987      */
16988     modalTitle : '',
16989     
16990     /**
16991      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16992      * field's data value (defaults to the underlying DOM element's name)
16993      */
16994     hiddenName: undefined,
16995     /**
16996      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16997      */
16998     listClass: '',
16999     /**
17000      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17001      */
17002     selectedClass: 'active',
17003     
17004     /**
17005      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17006      */
17007     shadow:'sides',
17008     /**
17009      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17010      * anchor positions (defaults to 'tl-bl')
17011      */
17012     listAlign: 'tl-bl?',
17013     /**
17014      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17015      */
17016     maxHeight: 300,
17017     /**
17018      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17019      * query specified by the allQuery config option (defaults to 'query')
17020      */
17021     triggerAction: 'query',
17022     /**
17023      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17024      * (defaults to 4, does not apply if editable = false)
17025      */
17026     minChars : 4,
17027     /**
17028      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17029      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17030      */
17031     typeAhead: false,
17032     /**
17033      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17034      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17035      */
17036     queryDelay: 500,
17037     /**
17038      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17039      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17040      */
17041     pageSize: 0,
17042     /**
17043      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17044      * when editable = true (defaults to false)
17045      */
17046     selectOnFocus:false,
17047     /**
17048      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17049      */
17050     queryParam: 'query',
17051     /**
17052      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17053      * when mode = 'remote' (defaults to 'Loading...')
17054      */
17055     loadingText: 'Loading...',
17056     /**
17057      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17058      */
17059     resizable: false,
17060     /**
17061      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17062      */
17063     handleHeight : 8,
17064     /**
17065      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17066      * traditional select (defaults to true)
17067      */
17068     editable: true,
17069     /**
17070      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17071      */
17072     allQuery: '',
17073     /**
17074      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17075      */
17076     mode: 'remote',
17077     /**
17078      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17079      * listWidth has a higher value)
17080      */
17081     minListWidth : 70,
17082     /**
17083      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17084      * allow the user to set arbitrary text into the field (defaults to false)
17085      */
17086     forceSelection:false,
17087     /**
17088      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17089      * if typeAhead = true (defaults to 250)
17090      */
17091     typeAheadDelay : 250,
17092     /**
17093      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17094      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17095      */
17096     valueNotFoundText : undefined,
17097     /**
17098      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17099      */
17100     blockFocus : false,
17101     
17102     /**
17103      * @cfg {Boolean} disableClear Disable showing of clear button.
17104      */
17105     disableClear : false,
17106     /**
17107      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17108      */
17109     alwaysQuery : false,
17110     
17111     /**
17112      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17113      */
17114     multiple : false,
17115     
17116     /**
17117      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17118      */
17119     invalidClass : "has-warning",
17120     
17121     /**
17122      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17123      */
17124     validClass : "has-success",
17125     
17126     /**
17127      * @cfg {Boolean} specialFilter (true|false) special filter default false
17128      */
17129     specialFilter : false,
17130     
17131     /**
17132      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17133      */
17134     mobileTouchView : true,
17135     
17136     /**
17137      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17138      */
17139     useNativeIOS : false,
17140     
17141     /**
17142      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17143      */
17144     mobile_restrict_height : false,
17145     
17146     ios_options : false,
17147     
17148     //private
17149     addicon : false,
17150     editicon: false,
17151     
17152     page: 0,
17153     hasQuery: false,
17154     append: false,
17155     loadNext: false,
17156     autoFocus : true,
17157     tickable : false,
17158     btnPosition : 'right',
17159     triggerList : true,
17160     showToggleBtn : true,
17161     animate : true,
17162     emptyResultText: 'Empty',
17163     triggerText : 'Select',
17164     emptyTitle : '',
17165     width : false,
17166     
17167     // element that contains real text value.. (when hidden is used..)
17168     
17169     getAutoCreate : function()
17170     {   
17171         var cfg = false;
17172         //render
17173         /*
17174          * Render classic select for iso
17175          */
17176         
17177         if(Roo.isIOS && this.useNativeIOS){
17178             cfg = this.getAutoCreateNativeIOS();
17179             return cfg;
17180         }
17181         
17182         /*
17183          * Touch Devices
17184          */
17185         
17186         if(Roo.isTouch && this.mobileTouchView){
17187             cfg = this.getAutoCreateTouchView();
17188             return cfg;;
17189         }
17190         
17191         /*
17192          *  Normal ComboBox
17193          */
17194         if(!this.tickable){
17195             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17196             return cfg;
17197         }
17198         
17199         /*
17200          *  ComboBox with tickable selections
17201          */
17202              
17203         var align = this.labelAlign || this.parentLabelAlign();
17204         
17205         cfg = {
17206             cls : 'form-group roo-combobox-tickable' //input-group
17207         };
17208         
17209         var btn_text_select = '';
17210         var btn_text_done = '';
17211         var btn_text_cancel = '';
17212         
17213         if (this.btn_text_show) {
17214             btn_text_select = 'Select';
17215             btn_text_done = 'Done';
17216             btn_text_cancel = 'Cancel'; 
17217         }
17218         
17219         var buttons = {
17220             tag : 'div',
17221             cls : 'tickable-buttons',
17222             cn : [
17223                 {
17224                     tag : 'button',
17225                     type : 'button',
17226                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17227                     //html : this.triggerText
17228                     html: btn_text_select
17229                 },
17230                 {
17231                     tag : 'button',
17232                     type : 'button',
17233                     name : 'ok',
17234                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17235                     //html : 'Done'
17236                     html: btn_text_done
17237                 },
17238                 {
17239                     tag : 'button',
17240                     type : 'button',
17241                     name : 'cancel',
17242                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17243                     //html : 'Cancel'
17244                     html: btn_text_cancel
17245                 }
17246             ]
17247         };
17248         
17249         if(this.editable){
17250             buttons.cn.unshift({
17251                 tag: 'input',
17252                 cls: 'roo-select2-search-field-input'
17253             });
17254         }
17255         
17256         var _this = this;
17257         
17258         Roo.each(buttons.cn, function(c){
17259             if (_this.size) {
17260                 c.cls += ' btn-' + _this.size;
17261             }
17262
17263             if (_this.disabled) {
17264                 c.disabled = true;
17265             }
17266         });
17267         
17268         var box = {
17269             tag: 'div',
17270             style : 'display: contents',
17271             cn: [
17272                 {
17273                     tag: 'input',
17274                     type : 'hidden',
17275                     cls: 'form-hidden-field'
17276                 },
17277                 {
17278                     tag: 'ul',
17279                     cls: 'roo-select2-choices',
17280                     cn:[
17281                         {
17282                             tag: 'li',
17283                             cls: 'roo-select2-search-field',
17284                             cn: [
17285                                 buttons
17286                             ]
17287                         }
17288                     ]
17289                 }
17290             ]
17291         };
17292         
17293         var combobox = {
17294             cls: 'roo-select2-container input-group roo-select2-container-multi',
17295             cn: [
17296                 
17297                 box
17298 //                {
17299 //                    tag: 'ul',
17300 //                    cls: 'typeahead typeahead-long dropdown-menu',
17301 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17302 //                }
17303             ]
17304         };
17305         
17306         if(this.hasFeedback && !this.allowBlank){
17307             
17308             var feedback = {
17309                 tag: 'span',
17310                 cls: 'glyphicon form-control-feedback'
17311             };
17312
17313             combobox.cn.push(feedback);
17314         }
17315         
17316         
17317         
17318         var indicator = {
17319             tag : 'i',
17320             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17321             tooltip : 'This field is required'
17322         };
17323          
17324         if (this.allowBlank) {
17325             indicator = {
17326                 tag : 'i',
17327                 style : 'display:none'
17328             };
17329         } 
17330         if (align ==='left' && this.fieldLabel.length) {
17331             
17332             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17333             
17334             cfg.cn = [
17335                 indicator,
17336                 {
17337                     tag: 'label',
17338                     'for' :  id,
17339                     cls : 'control-label col-form-label',
17340                     html : this.fieldLabel
17341
17342                 },
17343                 {
17344                     cls : "", 
17345                     cn: [
17346                         combobox
17347                     ]
17348                 }
17349
17350             ];
17351             
17352             var labelCfg = cfg.cn[1];
17353             var contentCfg = cfg.cn[2];
17354             
17355
17356             if(this.indicatorpos == 'right'){
17357                 
17358                 cfg.cn = [
17359                     {
17360                         tag: 'label',
17361                         'for' :  id,
17362                         cls : 'control-label col-form-label',
17363                         cn : [
17364                             {
17365                                 tag : 'span',
17366                                 html : this.fieldLabel
17367                             },
17368                             indicator
17369                         ]
17370                     },
17371                     {
17372                         cls : "",
17373                         cn: [
17374                             combobox
17375                         ]
17376                     }
17377
17378                 ];
17379                 
17380                 
17381                 
17382                 labelCfg = cfg.cn[0];
17383                 contentCfg = cfg.cn[1];
17384             
17385             }
17386             
17387             if(this.labelWidth > 12){
17388                 labelCfg.style = "width: " + this.labelWidth + 'px';
17389             }
17390             if(this.width * 1 > 0){
17391                 contentCfg.style = "width: " + this.width + 'px';
17392             }
17393             if(this.labelWidth < 13 && this.labelmd == 0){
17394                 this.labelmd = this.labelWidth;
17395             }
17396             
17397             if(this.labellg > 0){
17398                 labelCfg.cls += ' col-lg-' + this.labellg;
17399                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17400             }
17401             
17402             if(this.labelmd > 0){
17403                 labelCfg.cls += ' col-md-' + this.labelmd;
17404                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17405             }
17406             
17407             if(this.labelsm > 0){
17408                 labelCfg.cls += ' col-sm-' + this.labelsm;
17409                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17410             }
17411             
17412             if(this.labelxs > 0){
17413                 labelCfg.cls += ' col-xs-' + this.labelxs;
17414                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17415             }
17416                 
17417                 
17418         } else if ( this.fieldLabel.length) {
17419 //                Roo.log(" label");
17420                  cfg.cn = [
17421                    indicator,
17422                     {
17423                         tag: 'label',
17424                         //cls : 'input-group-addon',
17425                         html : this.fieldLabel
17426                     },
17427                     combobox
17428                 ];
17429                 
17430                 if(this.indicatorpos == 'right'){
17431                     cfg.cn = [
17432                         {
17433                             tag: 'label',
17434                             //cls : 'input-group-addon',
17435                             html : this.fieldLabel
17436                         },
17437                         indicator,
17438                         combobox
17439                     ];
17440                     
17441                 }
17442
17443         } else {
17444             
17445 //                Roo.log(" no label && no align");
17446                 cfg = combobox
17447                      
17448                 
17449         }
17450          
17451         var settings=this;
17452         ['xs','sm','md','lg'].map(function(size){
17453             if (settings[size]) {
17454                 cfg.cls += ' col-' + size + '-' + settings[size];
17455             }
17456         });
17457         
17458         return cfg;
17459         
17460     },
17461     
17462     _initEventsCalled : false,
17463     
17464     // private
17465     initEvents: function()
17466     {   
17467         if (this._initEventsCalled) { // as we call render... prevent looping...
17468             return;
17469         }
17470         this._initEventsCalled = true;
17471         
17472         if (!this.store) {
17473             throw "can not find store for combo";
17474         }
17475         
17476         this.indicator = this.indicatorEl();
17477         
17478         this.store = Roo.factory(this.store, Roo.data);
17479         this.store.parent = this;
17480         
17481         // if we are building from html. then this element is so complex, that we can not really
17482         // use the rendered HTML.
17483         // so we have to trash and replace the previous code.
17484         if (Roo.XComponent.build_from_html) {
17485             // remove this element....
17486             var e = this.el.dom, k=0;
17487             while (e ) { e = e.previousSibling;  ++k;}
17488
17489             this.el.remove();
17490             
17491             this.el=false;
17492             this.rendered = false;
17493             
17494             this.render(this.parent().getChildContainer(true), k);
17495         }
17496         
17497         if(Roo.isIOS && this.useNativeIOS){
17498             this.initIOSView();
17499             return;
17500         }
17501         
17502         /*
17503          * Touch Devices
17504          */
17505         
17506         if(Roo.isTouch && this.mobileTouchView){
17507             this.initTouchView();
17508             return;
17509         }
17510         
17511         if(this.tickable){
17512             this.initTickableEvents();
17513             return;
17514         }
17515         
17516         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17517         
17518         if(this.hiddenName){
17519             
17520             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17521             
17522             this.hiddenField.dom.value =
17523                 this.hiddenValue !== undefined ? this.hiddenValue :
17524                 this.value !== undefined ? this.value : '';
17525
17526             // prevent input submission
17527             this.el.dom.removeAttribute('name');
17528             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17529              
17530              
17531         }
17532         //if(Roo.isGecko){
17533         //    this.el.dom.setAttribute('autocomplete', 'off');
17534         //}
17535         
17536         var cls = 'x-combo-list';
17537         
17538         //this.list = new Roo.Layer({
17539         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17540         //});
17541         
17542         var _this = this;
17543         
17544         (function(){
17545             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17546             _this.list.setWidth(lw);
17547         }).defer(100);
17548         
17549         this.list.on('mouseover', this.onViewOver, this);
17550         this.list.on('mousemove', this.onViewMove, this);
17551         this.list.on('scroll', this.onViewScroll, this);
17552         
17553         /*
17554         this.list.swallowEvent('mousewheel');
17555         this.assetHeight = 0;
17556
17557         if(this.title){
17558             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17559             this.assetHeight += this.header.getHeight();
17560         }
17561
17562         this.innerList = this.list.createChild({cls:cls+'-inner'});
17563         this.innerList.on('mouseover', this.onViewOver, this);
17564         this.innerList.on('mousemove', this.onViewMove, this);
17565         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17566         
17567         if(this.allowBlank && !this.pageSize && !this.disableClear){
17568             this.footer = this.list.createChild({cls:cls+'-ft'});
17569             this.pageTb = new Roo.Toolbar(this.footer);
17570            
17571         }
17572         if(this.pageSize){
17573             this.footer = this.list.createChild({cls:cls+'-ft'});
17574             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17575                     {pageSize: this.pageSize});
17576             
17577         }
17578         
17579         if (this.pageTb && this.allowBlank && !this.disableClear) {
17580             var _this = this;
17581             this.pageTb.add(new Roo.Toolbar.Fill(), {
17582                 cls: 'x-btn-icon x-btn-clear',
17583                 text: '&#160;',
17584                 handler: function()
17585                 {
17586                     _this.collapse();
17587                     _this.clearValue();
17588                     _this.onSelect(false, -1);
17589                 }
17590             });
17591         }
17592         if (this.footer) {
17593             this.assetHeight += this.footer.getHeight();
17594         }
17595         */
17596             
17597         if(!this.tpl){
17598             this.tpl = Roo.bootstrap.version == 4 ?
17599                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17600                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17601         }
17602
17603         this.view = new Roo.View(this.list, this.tpl, {
17604             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17605         });
17606         //this.view.wrapEl.setDisplayed(false);
17607         this.view.on('click', this.onViewClick, this);
17608         
17609         
17610         this.store.on('beforeload', this.onBeforeLoad, this);
17611         this.store.on('load', this.onLoad, this);
17612         this.store.on('loadexception', this.onLoadException, this);
17613         /*
17614         if(this.resizable){
17615             this.resizer = new Roo.Resizable(this.list,  {
17616                pinned:true, handles:'se'
17617             });
17618             this.resizer.on('resize', function(r, w, h){
17619                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17620                 this.listWidth = w;
17621                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17622                 this.restrictHeight();
17623             }, this);
17624             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17625         }
17626         */
17627         if(!this.editable){
17628             this.editable = true;
17629             this.setEditable(false);
17630         }
17631         
17632         /*
17633         
17634         if (typeof(this.events.add.listeners) != 'undefined') {
17635             
17636             this.addicon = this.wrap.createChild(
17637                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17638        
17639             this.addicon.on('click', function(e) {
17640                 this.fireEvent('add', this);
17641             }, this);
17642         }
17643         if (typeof(this.events.edit.listeners) != 'undefined') {
17644             
17645             this.editicon = this.wrap.createChild(
17646                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17647             if (this.addicon) {
17648                 this.editicon.setStyle('margin-left', '40px');
17649             }
17650             this.editicon.on('click', function(e) {
17651                 
17652                 // we fire even  if inothing is selected..
17653                 this.fireEvent('edit', this, this.lastData );
17654                 
17655             }, this);
17656         }
17657         */
17658         
17659         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17660             "up" : function(e){
17661                 this.inKeyMode = true;
17662                 this.selectPrev();
17663             },
17664
17665             "down" : function(e){
17666                 if(!this.isExpanded()){
17667                     this.onTriggerClick();
17668                 }else{
17669                     this.inKeyMode = true;
17670                     this.selectNext();
17671                 }
17672             },
17673
17674             "enter" : function(e){
17675 //                this.onViewClick();
17676                 //return true;
17677                 this.collapse();
17678                 
17679                 if(this.fireEvent("specialkey", this, e)){
17680                     this.onViewClick(false);
17681                 }
17682                 
17683                 return true;
17684             },
17685
17686             "esc" : function(e){
17687                 this.collapse();
17688             },
17689
17690             "tab" : function(e){
17691                 this.collapse();
17692                 
17693                 if(this.fireEvent("specialkey", this, e)){
17694                     this.onViewClick(false);
17695                 }
17696                 
17697                 return true;
17698             },
17699
17700             scope : this,
17701
17702             doRelay : function(foo, bar, hname){
17703                 if(hname == 'down' || this.scope.isExpanded()){
17704                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17705                 }
17706                 return true;
17707             },
17708
17709             forceKeyDown: true
17710         });
17711         
17712         
17713         this.queryDelay = Math.max(this.queryDelay || 10,
17714                 this.mode == 'local' ? 10 : 250);
17715         
17716         
17717         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17718         
17719         if(this.typeAhead){
17720             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17721         }
17722         if(this.editable !== false){
17723             this.inputEl().on("keyup", this.onKeyUp, this);
17724         }
17725         if(this.forceSelection){
17726             this.inputEl().on('blur', this.doForce, this);
17727         }
17728         
17729         if(this.multiple){
17730             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17731             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17732         }
17733     },
17734     
17735     initTickableEvents: function()
17736     {   
17737         this.createList();
17738         
17739         if(this.hiddenName){
17740             
17741             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17742             
17743             this.hiddenField.dom.value =
17744                 this.hiddenValue !== undefined ? this.hiddenValue :
17745                 this.value !== undefined ? this.value : '';
17746
17747             // prevent input submission
17748             this.el.dom.removeAttribute('name');
17749             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17750              
17751              
17752         }
17753         
17754 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17755         
17756         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17757         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17758         if(this.triggerList){
17759             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17760         }
17761          
17762         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17763         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17764         
17765         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17766         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17767         
17768         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17769         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17770         
17771         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17772         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17773         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17774         
17775         this.okBtn.hide();
17776         this.cancelBtn.hide();
17777         
17778         var _this = this;
17779         
17780         (function(){
17781             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17782             _this.list.setWidth(lw);
17783         }).defer(100);
17784         
17785         this.list.on('mouseover', this.onViewOver, this);
17786         this.list.on('mousemove', this.onViewMove, this);
17787         
17788         this.list.on('scroll', this.onViewScroll, this);
17789         
17790         if(!this.tpl){
17791             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17792                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17793         }
17794
17795         this.view = new Roo.View(this.list, this.tpl, {
17796             singleSelect:true,
17797             tickable:true,
17798             parent:this,
17799             store: this.store,
17800             selectedClass: this.selectedClass
17801         });
17802         
17803         //this.view.wrapEl.setDisplayed(false);
17804         this.view.on('click', this.onViewClick, this);
17805         
17806         
17807         
17808         this.store.on('beforeload', this.onBeforeLoad, this);
17809         this.store.on('load', this.onLoad, this);
17810         this.store.on('loadexception', this.onLoadException, this);
17811         
17812         if(this.editable){
17813             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17814                 "up" : function(e){
17815                     this.inKeyMode = true;
17816                     this.selectPrev();
17817                 },
17818
17819                 "down" : function(e){
17820                     this.inKeyMode = true;
17821                     this.selectNext();
17822                 },
17823
17824                 "enter" : function(e){
17825                     if(this.fireEvent("specialkey", this, e)){
17826                         this.onViewClick(false);
17827                     }
17828                     
17829                     return true;
17830                 },
17831
17832                 "esc" : function(e){
17833                     this.onTickableFooterButtonClick(e, false, false);
17834                 },
17835
17836                 "tab" : function(e){
17837                     this.fireEvent("specialkey", this, e);
17838                     
17839                     this.onTickableFooterButtonClick(e, false, false);
17840                     
17841                     return true;
17842                 },
17843
17844                 scope : this,
17845
17846                 doRelay : function(e, fn, key){
17847                     if(this.scope.isExpanded()){
17848                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17849                     }
17850                     return true;
17851                 },
17852
17853                 forceKeyDown: true
17854             });
17855         }
17856         
17857         this.queryDelay = Math.max(this.queryDelay || 10,
17858                 this.mode == 'local' ? 10 : 250);
17859         
17860         
17861         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17862         
17863         if(this.typeAhead){
17864             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17865         }
17866         
17867         if(this.editable !== false){
17868             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17869         }
17870         
17871         this.indicator = this.indicatorEl();
17872         
17873         if(this.indicator){
17874             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17875             this.indicator.hide();
17876         }
17877         
17878     },
17879
17880     onDestroy : function(){
17881         if(this.view){
17882             this.view.setStore(null);
17883             this.view.el.removeAllListeners();
17884             this.view.el.remove();
17885             this.view.purgeListeners();
17886         }
17887         if(this.list){
17888             this.list.dom.innerHTML  = '';
17889         }
17890         
17891         if(this.store){
17892             this.store.un('beforeload', this.onBeforeLoad, this);
17893             this.store.un('load', this.onLoad, this);
17894             this.store.un('loadexception', this.onLoadException, this);
17895         }
17896         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17897     },
17898
17899     // private
17900     fireKey : function(e){
17901         if(e.isNavKeyPress() && !this.list.isVisible()){
17902             this.fireEvent("specialkey", this, e);
17903         }
17904     },
17905
17906     // private
17907     onResize: function(w, h)
17908     {
17909         
17910         
17911 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17912 //        
17913 //        if(typeof w != 'number'){
17914 //            // we do not handle it!?!?
17915 //            return;
17916 //        }
17917 //        var tw = this.trigger.getWidth();
17918 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17919 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17920 //        var x = w - tw;
17921 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17922 //            
17923 //        //this.trigger.setStyle('left', x+'px');
17924 //        
17925 //        if(this.list && this.listWidth === undefined){
17926 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17927 //            this.list.setWidth(lw);
17928 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17929 //        }
17930         
17931     
17932         
17933     },
17934
17935     /**
17936      * Allow or prevent the user from directly editing the field text.  If false is passed,
17937      * the user will only be able to select from the items defined in the dropdown list.  This method
17938      * is the runtime equivalent of setting the 'editable' config option at config time.
17939      * @param {Boolean} value True to allow the user to directly edit the field text
17940      */
17941     setEditable : function(value){
17942         if(value == this.editable){
17943             return;
17944         }
17945         this.editable = value;
17946         if(!value){
17947             this.inputEl().dom.setAttribute('readOnly', true);
17948             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17949             this.inputEl().addClass('x-combo-noedit');
17950         }else{
17951             this.inputEl().dom.removeAttribute('readOnly');
17952             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17953             this.inputEl().removeClass('x-combo-noedit');
17954         }
17955     },
17956
17957     // private
17958     
17959     onBeforeLoad : function(combo,opts){
17960         if(!this.hasFocus){
17961             return;
17962         }
17963          if (!opts.add) {
17964             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17965          }
17966         this.restrictHeight();
17967         this.selectedIndex = -1;
17968     },
17969
17970     // private
17971     onLoad : function(){
17972         
17973         this.hasQuery = false;
17974         
17975         if(!this.hasFocus){
17976             return;
17977         }
17978         
17979         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17980             this.loading.hide();
17981         }
17982         
17983         if(this.store.getCount() > 0){
17984             
17985             this.expand();
17986             this.restrictHeight();
17987             if(this.lastQuery == this.allQuery){
17988                 if(this.editable && !this.tickable){
17989                     this.inputEl().dom.select();
17990                 }
17991                 
17992                 if(
17993                     !this.selectByValue(this.value, true) &&
17994                     this.autoFocus && 
17995                     (
17996                         !this.store.lastOptions ||
17997                         typeof(this.store.lastOptions.add) == 'undefined' || 
17998                         this.store.lastOptions.add != true
17999                     )
18000                 ){
18001                     this.select(0, true);
18002                 }
18003             }else{
18004                 if(this.autoFocus){
18005                     this.selectNext();
18006                 }
18007                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18008                     this.taTask.delay(this.typeAheadDelay);
18009                 }
18010             }
18011         }else{
18012             this.onEmptyResults();
18013         }
18014         
18015         //this.el.focus();
18016     },
18017     // private
18018     onLoadException : function()
18019     {
18020         this.hasQuery = false;
18021         
18022         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18023             this.loading.hide();
18024         }
18025         
18026         if(this.tickable && this.editable){
18027             return;
18028         }
18029         
18030         this.collapse();
18031         // only causes errors at present
18032         //Roo.log(this.store.reader.jsonData);
18033         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18034             // fixme
18035             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18036         //}
18037         
18038         
18039     },
18040     // private
18041     onTypeAhead : function(){
18042         if(this.store.getCount() > 0){
18043             var r = this.store.getAt(0);
18044             var newValue = r.data[this.displayField];
18045             var len = newValue.length;
18046             var selStart = this.getRawValue().length;
18047             
18048             if(selStart != len){
18049                 this.setRawValue(newValue);
18050                 this.selectText(selStart, newValue.length);
18051             }
18052         }
18053     },
18054
18055     // private
18056     onSelect : function(record, index){
18057         
18058         if(this.fireEvent('beforeselect', this, record, index) !== false){
18059         
18060             this.setFromData(index > -1 ? record.data : false);
18061             
18062             this.collapse();
18063             this.fireEvent('select', this, record, index);
18064         }
18065     },
18066
18067     /**
18068      * Returns the currently selected field value or empty string if no value is set.
18069      * @return {String} value The selected value
18070      */
18071     getValue : function()
18072     {
18073         if(Roo.isIOS && this.useNativeIOS){
18074             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18075         }
18076         
18077         if(this.multiple){
18078             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18079         }
18080         
18081         if(this.valueField){
18082             return typeof this.value != 'undefined' ? this.value : '';
18083         }else{
18084             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18085         }
18086     },
18087     
18088     getRawValue : function()
18089     {
18090         if(Roo.isIOS && this.useNativeIOS){
18091             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18092         }
18093         
18094         var v = this.inputEl().getValue();
18095         
18096         return v;
18097     },
18098
18099     /**
18100      * Clears any text/value currently set in the field
18101      */
18102     clearValue : function(){
18103         
18104         if(this.hiddenField){
18105             this.hiddenField.dom.value = '';
18106         }
18107         this.value = '';
18108         this.setRawValue('');
18109         this.lastSelectionText = '';
18110         this.lastData = false;
18111         
18112         var close = this.closeTriggerEl();
18113         
18114         if(close){
18115             close.hide();
18116         }
18117         
18118         this.validate();
18119         
18120     },
18121
18122     /**
18123      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18124      * will be displayed in the field.  If the value does not match the data value of an existing item,
18125      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18126      * Otherwise the field will be blank (although the value will still be set).
18127      * @param {String} value The value to match
18128      */
18129     setValue : function(v)
18130     {
18131         if(Roo.isIOS && this.useNativeIOS){
18132             this.setIOSValue(v);
18133             return;
18134         }
18135         
18136         if(this.multiple){
18137             this.syncValue();
18138             return;
18139         }
18140         
18141         var text = v;
18142         if(this.valueField){
18143             var r = this.findRecord(this.valueField, v);
18144             if(r){
18145                 text = r.data[this.displayField];
18146             }else if(this.valueNotFoundText !== undefined){
18147                 text = this.valueNotFoundText;
18148             }
18149         }
18150         this.lastSelectionText = text;
18151         if(this.hiddenField){
18152             this.hiddenField.dom.value = v;
18153         }
18154         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18155         this.value = v;
18156         
18157         var close = this.closeTriggerEl();
18158         
18159         if(close){
18160             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18161         }
18162         
18163         this.validate();
18164     },
18165     /**
18166      * @property {Object} the last set data for the element
18167      */
18168     
18169     lastData : false,
18170     /**
18171      * Sets the value of the field based on a object which is related to the record format for the store.
18172      * @param {Object} value the value to set as. or false on reset?
18173      */
18174     setFromData : function(o){
18175         
18176         if(this.multiple){
18177             this.addItem(o);
18178             return;
18179         }
18180             
18181         var dv = ''; // display value
18182         var vv = ''; // value value..
18183         this.lastData = o;
18184         if (this.displayField) {
18185             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18186         } else {
18187             // this is an error condition!!!
18188             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18189         }
18190         
18191         if(this.valueField){
18192             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18193         }
18194         
18195         var close = this.closeTriggerEl();
18196         
18197         if(close){
18198             if(dv.length || vv * 1 > 0){
18199                 close.show() ;
18200                 this.blockFocus=true;
18201             } else {
18202                 close.hide();
18203             }             
18204         }
18205         
18206         if(this.hiddenField){
18207             this.hiddenField.dom.value = vv;
18208             
18209             this.lastSelectionText = dv;
18210             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18211             this.value = vv;
18212             return;
18213         }
18214         // no hidden field.. - we store the value in 'value', but still display
18215         // display field!!!!
18216         this.lastSelectionText = dv;
18217         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18218         this.value = vv;
18219         
18220         
18221         
18222     },
18223     // private
18224     reset : function(){
18225         // overridden so that last data is reset..
18226         
18227         if(this.multiple){
18228             this.clearItem();
18229             return;
18230         }
18231         
18232         this.setValue(this.originalValue);
18233         //this.clearInvalid();
18234         this.lastData = false;
18235         if (this.view) {
18236             this.view.clearSelections();
18237         }
18238         
18239         this.validate();
18240     },
18241     // private
18242     findRecord : function(prop, value){
18243         var record;
18244         if(this.store.getCount() > 0){
18245             this.store.each(function(r){
18246                 if(r.data[prop] == value){
18247                     record = r;
18248                     return false;
18249                 }
18250                 return true;
18251             });
18252         }
18253         return record;
18254     },
18255     
18256     getName: function()
18257     {
18258         // returns hidden if it's set..
18259         if (!this.rendered) {return ''};
18260         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18261         
18262     },
18263     // private
18264     onViewMove : function(e, t){
18265         this.inKeyMode = false;
18266     },
18267
18268     // private
18269     onViewOver : function(e, t){
18270         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18271             return;
18272         }
18273         var item = this.view.findItemFromChild(t);
18274         
18275         if(item){
18276             var index = this.view.indexOf(item);
18277             this.select(index, false);
18278         }
18279     },
18280
18281     // private
18282     onViewClick : function(view, doFocus, el, e)
18283     {
18284         var index = this.view.getSelectedIndexes()[0];
18285         
18286         var r = this.store.getAt(index);
18287         
18288         if(this.tickable){
18289             
18290             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18291                 return;
18292             }
18293             
18294             var rm = false;
18295             var _this = this;
18296             
18297             Roo.each(this.tickItems, function(v,k){
18298                 
18299                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18300                     Roo.log(v);
18301                     _this.tickItems.splice(k, 1);
18302                     
18303                     if(typeof(e) == 'undefined' && view == false){
18304                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18305                     }
18306                     
18307                     rm = true;
18308                     return;
18309                 }
18310             });
18311             
18312             if(rm){
18313                 return;
18314             }
18315             
18316             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18317                 this.tickItems.push(r.data);
18318             }
18319             
18320             if(typeof(e) == 'undefined' && view == false){
18321                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18322             }
18323                     
18324             return;
18325         }
18326         
18327         if(r){
18328             this.onSelect(r, index);
18329         }
18330         if(doFocus !== false && !this.blockFocus){
18331             this.inputEl().focus();
18332         }
18333     },
18334
18335     // private
18336     restrictHeight : function(){
18337         //this.innerList.dom.style.height = '';
18338         //var inner = this.innerList.dom;
18339         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18340         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18341         //this.list.beginUpdate();
18342         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18343         this.list.alignTo(this.inputEl(), this.listAlign);
18344         this.list.alignTo(this.inputEl(), this.listAlign);
18345         //this.list.endUpdate();
18346     },
18347
18348     // private
18349     onEmptyResults : function(){
18350         
18351         if(this.tickable && this.editable){
18352             this.hasFocus = false;
18353             this.restrictHeight();
18354             return;
18355         }
18356         
18357         this.collapse();
18358     },
18359
18360     /**
18361      * Returns true if the dropdown list is expanded, else false.
18362      */
18363     isExpanded : function(){
18364         return this.list.isVisible();
18365     },
18366
18367     /**
18368      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18369      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18370      * @param {String} value The data value of the item to select
18371      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18372      * selected item if it is not currently in view (defaults to true)
18373      * @return {Boolean} True if the value matched an item in the list, else false
18374      */
18375     selectByValue : function(v, scrollIntoView){
18376         if(v !== undefined && v !== null){
18377             var r = this.findRecord(this.valueField || this.displayField, v);
18378             if(r){
18379                 this.select(this.store.indexOf(r), scrollIntoView);
18380                 return true;
18381             }
18382         }
18383         return false;
18384     },
18385
18386     /**
18387      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18388      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18389      * @param {Number} index The zero-based index of the list item to select
18390      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18391      * selected item if it is not currently in view (defaults to true)
18392      */
18393     select : function(index, scrollIntoView){
18394         this.selectedIndex = index;
18395         this.view.select(index);
18396         if(scrollIntoView !== false){
18397             var el = this.view.getNode(index);
18398             /*
18399              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18400              */
18401             if(el){
18402                 this.list.scrollChildIntoView(el, false);
18403             }
18404         }
18405     },
18406
18407     // private
18408     selectNext : function(){
18409         var ct = this.store.getCount();
18410         if(ct > 0){
18411             if(this.selectedIndex == -1){
18412                 this.select(0);
18413             }else if(this.selectedIndex < ct-1){
18414                 this.select(this.selectedIndex+1);
18415             }
18416         }
18417     },
18418
18419     // private
18420     selectPrev : function(){
18421         var ct = this.store.getCount();
18422         if(ct > 0){
18423             if(this.selectedIndex == -1){
18424                 this.select(0);
18425             }else if(this.selectedIndex != 0){
18426                 this.select(this.selectedIndex-1);
18427             }
18428         }
18429     },
18430
18431     // private
18432     onKeyUp : function(e){
18433         if(this.editable !== false && !e.isSpecialKey()){
18434             this.lastKey = e.getKey();
18435             this.dqTask.delay(this.queryDelay);
18436         }
18437     },
18438
18439     // private
18440     validateBlur : function(){
18441         return !this.list || !this.list.isVisible();   
18442     },
18443
18444     // private
18445     initQuery : function(){
18446         
18447         var v = this.getRawValue();
18448         
18449         if(this.tickable && this.editable){
18450             v = this.tickableInputEl().getValue();
18451         }
18452         
18453         this.doQuery(v);
18454     },
18455
18456     // private
18457     doForce : function(){
18458         if(this.inputEl().dom.value.length > 0){
18459             this.inputEl().dom.value =
18460                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18461              
18462         }
18463     },
18464
18465     /**
18466      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18467      * query allowing the query action to be canceled if needed.
18468      * @param {String} query The SQL query to execute
18469      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18470      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18471      * saved in the current store (defaults to false)
18472      */
18473     doQuery : function(q, forceAll){
18474         
18475         if(q === undefined || q === null){
18476             q = '';
18477         }
18478         var qe = {
18479             query: q,
18480             forceAll: forceAll,
18481             combo: this,
18482             cancel:false
18483         };
18484         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18485             return false;
18486         }
18487         q = qe.query;
18488         
18489         forceAll = qe.forceAll;
18490         if(forceAll === true || (q.length >= this.minChars)){
18491             
18492             this.hasQuery = true;
18493             
18494             if(this.lastQuery != q || this.alwaysQuery){
18495                 this.lastQuery = q;
18496                 if(this.mode == 'local'){
18497                     this.selectedIndex = -1;
18498                     if(forceAll){
18499                         this.store.clearFilter();
18500                     }else{
18501                         
18502                         if(this.specialFilter){
18503                             this.fireEvent('specialfilter', this);
18504                             this.onLoad();
18505                             return;
18506                         }
18507                         
18508                         this.store.filter(this.displayField, q);
18509                     }
18510                     
18511                     this.store.fireEvent("datachanged", this.store);
18512                     
18513                     this.onLoad();
18514                     
18515                     
18516                 }else{
18517                     
18518                     this.store.baseParams[this.queryParam] = q;
18519                     
18520                     var options = {params : this.getParams(q)};
18521                     
18522                     if(this.loadNext){
18523                         options.add = true;
18524                         options.params.start = this.page * this.pageSize;
18525                     }
18526                     
18527                     this.store.load(options);
18528                     
18529                     /*
18530                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18531                      *  we should expand the list on onLoad
18532                      *  so command out it
18533                      */
18534 //                    this.expand();
18535                 }
18536             }else{
18537                 this.selectedIndex = -1;
18538                 this.onLoad();   
18539             }
18540         }
18541         
18542         this.loadNext = false;
18543     },
18544     
18545     // private
18546     getParams : function(q){
18547         var p = {};
18548         //p[this.queryParam] = q;
18549         
18550         if(this.pageSize){
18551             p.start = 0;
18552             p.limit = this.pageSize;
18553         }
18554         return p;
18555     },
18556
18557     /**
18558      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18559      */
18560     collapse : function(){
18561         if(!this.isExpanded()){
18562             return;
18563         }
18564         
18565         this.list.hide();
18566         
18567         this.hasFocus = false;
18568         
18569         if(this.tickable){
18570             this.okBtn.hide();
18571             this.cancelBtn.hide();
18572             this.trigger.show();
18573             
18574             if(this.editable){
18575                 this.tickableInputEl().dom.value = '';
18576                 this.tickableInputEl().blur();
18577             }
18578             
18579         }
18580         
18581         Roo.get(document).un('mousedown', this.collapseIf, this);
18582         Roo.get(document).un('mousewheel', this.collapseIf, this);
18583         if (!this.editable) {
18584             Roo.get(document).un('keydown', this.listKeyPress, this);
18585         }
18586         this.fireEvent('collapse', this);
18587         
18588         this.validate();
18589     },
18590
18591     // private
18592     collapseIf : function(e){
18593         var in_combo  = e.within(this.el);
18594         var in_list =  e.within(this.list);
18595         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18596         
18597         if (in_combo || in_list || is_list) {
18598             //e.stopPropagation();
18599             return;
18600         }
18601         
18602         if(this.tickable){
18603             this.onTickableFooterButtonClick(e, false, false);
18604         }
18605
18606         this.collapse();
18607         
18608     },
18609
18610     /**
18611      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18612      */
18613     expand : function(){
18614        
18615         if(this.isExpanded() || !this.hasFocus){
18616             return;
18617         }
18618         
18619         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18620         this.list.setWidth(lw);
18621         
18622         Roo.log('expand');
18623         
18624         this.list.show();
18625         
18626         this.restrictHeight();
18627         
18628         if(this.tickable){
18629             
18630             this.tickItems = Roo.apply([], this.item);
18631             
18632             this.okBtn.show();
18633             this.cancelBtn.show();
18634             this.trigger.hide();
18635             
18636             if(this.editable){
18637                 this.tickableInputEl().focus();
18638             }
18639             
18640         }
18641         
18642         Roo.get(document).on('mousedown', this.collapseIf, this);
18643         Roo.get(document).on('mousewheel', this.collapseIf, this);
18644         if (!this.editable) {
18645             Roo.get(document).on('keydown', this.listKeyPress, this);
18646         }
18647         
18648         this.fireEvent('expand', this);
18649     },
18650
18651     // private
18652     // Implements the default empty TriggerField.onTriggerClick function
18653     onTriggerClick : function(e)
18654     {
18655         Roo.log('trigger click');
18656         
18657         if(this.disabled || !this.triggerList){
18658             return;
18659         }
18660         
18661         this.page = 0;
18662         this.loadNext = false;
18663         
18664         if(this.isExpanded()){
18665             this.collapse();
18666             if (!this.blockFocus) {
18667                 this.inputEl().focus();
18668             }
18669             
18670         }else {
18671             this.hasFocus = true;
18672             if(this.triggerAction == 'all') {
18673                 this.doQuery(this.allQuery, true);
18674             } else {
18675                 this.doQuery(this.getRawValue());
18676             }
18677             if (!this.blockFocus) {
18678                 this.inputEl().focus();
18679             }
18680         }
18681     },
18682     
18683     onTickableTriggerClick : function(e)
18684     {
18685         if(this.disabled){
18686             return;
18687         }
18688         
18689         this.page = 0;
18690         this.loadNext = false;
18691         this.hasFocus = true;
18692         
18693         if(this.triggerAction == 'all') {
18694             this.doQuery(this.allQuery, true);
18695         } else {
18696             this.doQuery(this.getRawValue());
18697         }
18698     },
18699     
18700     onSearchFieldClick : function(e)
18701     {
18702         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18703             this.onTickableFooterButtonClick(e, false, false);
18704             return;
18705         }
18706         
18707         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18708             return;
18709         }
18710         
18711         this.page = 0;
18712         this.loadNext = false;
18713         this.hasFocus = true;
18714         
18715         if(this.triggerAction == 'all') {
18716             this.doQuery(this.allQuery, true);
18717         } else {
18718             this.doQuery(this.getRawValue());
18719         }
18720     },
18721     
18722     listKeyPress : function(e)
18723     {
18724         //Roo.log('listkeypress');
18725         // scroll to first matching element based on key pres..
18726         if (e.isSpecialKey()) {
18727             return false;
18728         }
18729         var k = String.fromCharCode(e.getKey()).toUpperCase();
18730         //Roo.log(k);
18731         var match  = false;
18732         var csel = this.view.getSelectedNodes();
18733         var cselitem = false;
18734         if (csel.length) {
18735             var ix = this.view.indexOf(csel[0]);
18736             cselitem  = this.store.getAt(ix);
18737             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18738                 cselitem = false;
18739             }
18740             
18741         }
18742         
18743         this.store.each(function(v) { 
18744             if (cselitem) {
18745                 // start at existing selection.
18746                 if (cselitem.id == v.id) {
18747                     cselitem = false;
18748                 }
18749                 return true;
18750             }
18751                 
18752             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18753                 match = this.store.indexOf(v);
18754                 return false;
18755             }
18756             return true;
18757         }, this);
18758         
18759         if (match === false) {
18760             return true; // no more action?
18761         }
18762         // scroll to?
18763         this.view.select(match);
18764         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18765         sn.scrollIntoView(sn.dom.parentNode, false);
18766     },
18767     
18768     onViewScroll : function(e, t){
18769         
18770         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){
18771             return;
18772         }
18773         
18774         this.hasQuery = true;
18775         
18776         this.loading = this.list.select('.loading', true).first();
18777         
18778         if(this.loading === null){
18779             this.list.createChild({
18780                 tag: 'div',
18781                 cls: 'loading roo-select2-more-results roo-select2-active',
18782                 html: 'Loading more results...'
18783             });
18784             
18785             this.loading = this.list.select('.loading', true).first();
18786             
18787             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18788             
18789             this.loading.hide();
18790         }
18791         
18792         this.loading.show();
18793         
18794         var _combo = this;
18795         
18796         this.page++;
18797         this.loadNext = true;
18798         
18799         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18800         
18801         return;
18802     },
18803     
18804     addItem : function(o)
18805     {   
18806         var dv = ''; // display value
18807         
18808         if (this.displayField) {
18809             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18810         } else {
18811             // this is an error condition!!!
18812             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18813         }
18814         
18815         if(!dv.length){
18816             return;
18817         }
18818         
18819         var choice = this.choices.createChild({
18820             tag: 'li',
18821             cls: 'roo-select2-search-choice',
18822             cn: [
18823                 {
18824                     tag: 'div',
18825                     html: dv
18826                 },
18827                 {
18828                     tag: 'a',
18829                     href: '#',
18830                     cls: 'roo-select2-search-choice-close fa fa-times',
18831                     tabindex: '-1'
18832                 }
18833             ]
18834             
18835         }, this.searchField);
18836         
18837         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18838         
18839         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18840         
18841         this.item.push(o);
18842         
18843         this.lastData = o;
18844         
18845         this.syncValue();
18846         
18847         this.inputEl().dom.value = '';
18848         
18849         this.validate();
18850     },
18851     
18852     onRemoveItem : function(e, _self, o)
18853     {
18854         e.preventDefault();
18855         
18856         this.lastItem = Roo.apply([], this.item);
18857         
18858         var index = this.item.indexOf(o.data) * 1;
18859         
18860         if( index < 0){
18861             Roo.log('not this item?!');
18862             return;
18863         }
18864         
18865         this.item.splice(index, 1);
18866         o.item.remove();
18867         
18868         this.syncValue();
18869         
18870         this.fireEvent('remove', this, e);
18871         
18872         this.validate();
18873         
18874     },
18875     
18876     syncValue : function()
18877     {
18878         if(!this.item.length){
18879             this.clearValue();
18880             return;
18881         }
18882             
18883         var value = [];
18884         var _this = this;
18885         Roo.each(this.item, function(i){
18886             if(_this.valueField){
18887                 value.push(i[_this.valueField]);
18888                 return;
18889             }
18890
18891             value.push(i);
18892         });
18893
18894         this.value = value.join(',');
18895
18896         if(this.hiddenField){
18897             this.hiddenField.dom.value = this.value;
18898         }
18899         
18900         this.store.fireEvent("datachanged", this.store);
18901         
18902         this.validate();
18903     },
18904     
18905     clearItem : function()
18906     {
18907         if(!this.multiple){
18908             return;
18909         }
18910         
18911         this.item = [];
18912         
18913         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18914            c.remove();
18915         });
18916         
18917         this.syncValue();
18918         
18919         this.validate();
18920         
18921         if(this.tickable && !Roo.isTouch){
18922             this.view.refresh();
18923         }
18924     },
18925     
18926     inputEl: function ()
18927     {
18928         if(Roo.isIOS && this.useNativeIOS){
18929             return this.el.select('select.roo-ios-select', true).first();
18930         }
18931         
18932         if(Roo.isTouch && this.mobileTouchView){
18933             return this.el.select('input.form-control',true).first();
18934         }
18935         
18936         if(this.tickable){
18937             return this.searchField;
18938         }
18939         
18940         return this.el.select('input.form-control',true).first();
18941     },
18942     
18943     onTickableFooterButtonClick : function(e, btn, el)
18944     {
18945         e.preventDefault();
18946         
18947         this.lastItem = Roo.apply([], this.item);
18948         
18949         if(btn && btn.name == 'cancel'){
18950             this.tickItems = Roo.apply([], this.item);
18951             this.collapse();
18952             return;
18953         }
18954         
18955         this.clearItem();
18956         
18957         var _this = this;
18958         
18959         Roo.each(this.tickItems, function(o){
18960             _this.addItem(o);
18961         });
18962         
18963         this.collapse();
18964         
18965     },
18966     
18967     validate : function()
18968     {
18969         if(this.getVisibilityEl().hasClass('hidden')){
18970             return true;
18971         }
18972         
18973         var v = this.getRawValue();
18974         
18975         if(this.multiple){
18976             v = this.getValue();
18977         }
18978         
18979         if(this.disabled || this.allowBlank || v.length){
18980             this.markValid();
18981             return true;
18982         }
18983         
18984         this.markInvalid();
18985         return false;
18986     },
18987     
18988     tickableInputEl : function()
18989     {
18990         if(!this.tickable || !this.editable){
18991             return this.inputEl();
18992         }
18993         
18994         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18995     },
18996     
18997     
18998     getAutoCreateTouchView : function()
18999     {
19000         var id = Roo.id();
19001         
19002         var cfg = {
19003             cls: 'form-group' //input-group
19004         };
19005         
19006         var input =  {
19007             tag: 'input',
19008             id : id,
19009             type : this.inputType,
19010             cls : 'form-control x-combo-noedit',
19011             autocomplete: 'new-password',
19012             placeholder : this.placeholder || '',
19013             readonly : true
19014         };
19015         
19016         if (this.name) {
19017             input.name = this.name;
19018         }
19019         
19020         if (this.size) {
19021             input.cls += ' input-' + this.size;
19022         }
19023         
19024         if (this.disabled) {
19025             input.disabled = true;
19026         }
19027         
19028         var inputblock = {
19029             cls : 'roo-combobox-wrap',
19030             cn : [
19031                 input
19032             ]
19033         };
19034         
19035         if(this.before){
19036             inputblock.cls += ' input-group';
19037             
19038             inputblock.cn.unshift({
19039                 tag :'span',
19040                 cls : 'input-group-addon input-group-prepend input-group-text',
19041                 html : this.before
19042             });
19043         }
19044         
19045         if(this.removable && !this.multiple){
19046             inputblock.cls += ' roo-removable';
19047             
19048             inputblock.cn.push({
19049                 tag: 'button',
19050                 html : 'x',
19051                 cls : 'roo-combo-removable-btn close'
19052             });
19053         }
19054
19055         if(this.hasFeedback && !this.allowBlank){
19056             
19057             inputblock.cls += ' has-feedback';
19058             
19059             inputblock.cn.push({
19060                 tag: 'span',
19061                 cls: 'glyphicon form-control-feedback'
19062             });
19063             
19064         }
19065         
19066         if (this.after) {
19067             
19068             inputblock.cls += (this.before) ? '' : ' input-group';
19069             
19070             inputblock.cn.push({
19071                 tag :'span',
19072                 cls : 'input-group-addon input-group-append input-group-text',
19073                 html : this.after
19074             });
19075         }
19076
19077         
19078         var ibwrap = inputblock;
19079         
19080         if(this.multiple){
19081             ibwrap = {
19082                 tag: 'ul',
19083                 cls: 'roo-select2-choices',
19084                 cn:[
19085                     {
19086                         tag: 'li',
19087                         cls: 'roo-select2-search-field',
19088                         cn: [
19089
19090                             inputblock
19091                         ]
19092                     }
19093                 ]
19094             };
19095         
19096             
19097         }
19098         
19099         var combobox = {
19100             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19101             cn: [
19102                 {
19103                     tag: 'input',
19104                     type : 'hidden',
19105                     cls: 'form-hidden-field'
19106                 },
19107                 ibwrap
19108             ]
19109         };
19110         
19111         if(!this.multiple && this.showToggleBtn){
19112             
19113             var caret = {
19114                 cls: 'caret'
19115             };
19116             
19117             if (this.caret != false) {
19118                 caret = {
19119                      tag: 'i',
19120                      cls: 'fa fa-' + this.caret
19121                 };
19122                 
19123             }
19124             
19125             combobox.cn.push({
19126                 tag :'span',
19127                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19128                 cn : [
19129                     Roo.bootstrap.version == 3 ? caret : '',
19130                     {
19131                         tag: 'span',
19132                         cls: 'combobox-clear',
19133                         cn  : [
19134                             {
19135                                 tag : 'i',
19136                                 cls: 'icon-remove'
19137                             }
19138                         ]
19139                     }
19140                 ]
19141
19142             })
19143         }
19144         
19145         if(this.multiple){
19146             combobox.cls += ' roo-select2-container-multi';
19147         }
19148         
19149         var required =  this.allowBlank ?  {
19150                     tag : 'i',
19151                     style: 'display: none'
19152                 } : {
19153                    tag : 'i',
19154                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19155                    tooltip : 'This field is required'
19156                 };
19157         
19158         var align = this.labelAlign || this.parentLabelAlign();
19159         
19160         if (align ==='left' && this.fieldLabel.length) {
19161
19162             cfg.cn = [
19163                 required,
19164                 {
19165                     tag: 'label',
19166                     cls : 'control-label col-form-label',
19167                     html : this.fieldLabel
19168
19169                 },
19170                 {
19171                     cls : 'roo-combobox-wrap ', 
19172                     cn: [
19173                         combobox
19174                     ]
19175                 }
19176             ];
19177             
19178             var labelCfg = cfg.cn[1];
19179             var contentCfg = cfg.cn[2];
19180             
19181
19182             if(this.indicatorpos == 'right'){
19183                 cfg.cn = [
19184                     {
19185                         tag: 'label',
19186                         'for' :  id,
19187                         cls : 'control-label col-form-label',
19188                         cn : [
19189                             {
19190                                 tag : 'span',
19191                                 html : this.fieldLabel
19192                             },
19193                             required
19194                         ]
19195                     },
19196                     {
19197                         cls : "roo-combobox-wrap ",
19198                         cn: [
19199                             combobox
19200                         ]
19201                     }
19202
19203                 ];
19204                 
19205                 labelCfg = cfg.cn[0];
19206                 contentCfg = cfg.cn[1];
19207             }
19208             
19209            
19210             
19211             if(this.labelWidth > 12){
19212                 labelCfg.style = "width: " + this.labelWidth + 'px';
19213             }
19214            
19215             if(this.labelWidth < 13 && this.labelmd == 0){
19216                 this.labelmd = this.labelWidth;
19217             }
19218             
19219             if(this.labellg > 0){
19220                 labelCfg.cls += ' col-lg-' + this.labellg;
19221                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19222             }
19223             
19224             if(this.labelmd > 0){
19225                 labelCfg.cls += ' col-md-' + this.labelmd;
19226                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19227             }
19228             
19229             if(this.labelsm > 0){
19230                 labelCfg.cls += ' col-sm-' + this.labelsm;
19231                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19232             }
19233             
19234             if(this.labelxs > 0){
19235                 labelCfg.cls += ' col-xs-' + this.labelxs;
19236                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19237             }
19238                 
19239                 
19240         } else if ( this.fieldLabel.length) {
19241             cfg.cn = [
19242                required,
19243                 {
19244                     tag: 'label',
19245                     cls : 'control-label',
19246                     html : this.fieldLabel
19247
19248                 },
19249                 {
19250                     cls : '', 
19251                     cn: [
19252                         combobox
19253                     ]
19254                 }
19255             ];
19256             
19257             if(this.indicatorpos == 'right'){
19258                 cfg.cn = [
19259                     {
19260                         tag: 'label',
19261                         cls : 'control-label',
19262                         html : this.fieldLabel,
19263                         cn : [
19264                             required
19265                         ]
19266                     },
19267                     {
19268                         cls : '', 
19269                         cn: [
19270                             combobox
19271                         ]
19272                     }
19273                 ];
19274             }
19275         } else {
19276             cfg.cn = combobox;    
19277         }
19278         
19279         
19280         var settings = this;
19281         
19282         ['xs','sm','md','lg'].map(function(size){
19283             if (settings[size]) {
19284                 cfg.cls += ' col-' + size + '-' + settings[size];
19285             }
19286         });
19287         
19288         return cfg;
19289     },
19290     
19291     initTouchView : function()
19292     {
19293         this.renderTouchView();
19294         
19295         this.touchViewEl.on('scroll', function(){
19296             this.el.dom.scrollTop = 0;
19297         }, this);
19298         
19299         this.originalValue = this.getValue();
19300         
19301         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19302         
19303         this.inputEl().on("click", this.showTouchView, this);
19304         if (this.triggerEl) {
19305             this.triggerEl.on("click", this.showTouchView, this);
19306         }
19307         
19308         
19309         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19310         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19311         
19312         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19313         
19314         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19315         this.store.on('load', this.onTouchViewLoad, this);
19316         this.store.on('loadexception', this.onTouchViewLoadException, this);
19317         
19318         if(this.hiddenName){
19319             
19320             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19321             
19322             this.hiddenField.dom.value =
19323                 this.hiddenValue !== undefined ? this.hiddenValue :
19324                 this.value !== undefined ? this.value : '';
19325         
19326             this.el.dom.removeAttribute('name');
19327             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19328         }
19329         
19330         if(this.multiple){
19331             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19332             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19333         }
19334         
19335         if(this.removable && !this.multiple){
19336             var close = this.closeTriggerEl();
19337             if(close){
19338                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19339                 close.on('click', this.removeBtnClick, this, close);
19340             }
19341         }
19342         /*
19343          * fix the bug in Safari iOS8
19344          */
19345         this.inputEl().on("focus", function(e){
19346             document.activeElement.blur();
19347         }, this);
19348         
19349         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19350         
19351         return;
19352         
19353         
19354     },
19355     
19356     renderTouchView : function()
19357     {
19358         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19359         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19360         
19361         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19362         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19363         
19364         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19365         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19366         this.touchViewBodyEl.setStyle('overflow', 'auto');
19367         
19368         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19369         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19370         
19371         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19372         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19373         
19374     },
19375     
19376     showTouchView : function()
19377     {
19378         if(this.disabled){
19379             return;
19380         }
19381         
19382         this.touchViewHeaderEl.hide();
19383
19384         if(this.modalTitle.length){
19385             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19386             this.touchViewHeaderEl.show();
19387         }
19388
19389         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19390         this.touchViewEl.show();
19391
19392         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19393         
19394         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19395         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19396
19397         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19398
19399         if(this.modalTitle.length){
19400             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19401         }
19402         
19403         this.touchViewBodyEl.setHeight(bodyHeight);
19404
19405         if(this.animate){
19406             var _this = this;
19407             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19408         }else{
19409             this.touchViewEl.addClass(['in','show']);
19410         }
19411         
19412         if(this._touchViewMask){
19413             Roo.get(document.body).addClass("x-body-masked");
19414             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19415             this._touchViewMask.setStyle('z-index', 10000);
19416             this._touchViewMask.addClass('show');
19417         }
19418         
19419         this.doTouchViewQuery();
19420         
19421     },
19422     
19423     hideTouchView : function()
19424     {
19425         this.touchViewEl.removeClass(['in','show']);
19426
19427         if(this.animate){
19428             var _this = this;
19429             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19430         }else{
19431             this.touchViewEl.setStyle('display', 'none');
19432         }
19433         
19434         if(this._touchViewMask){
19435             this._touchViewMask.removeClass('show');
19436             Roo.get(document.body).removeClass("x-body-masked");
19437         }
19438     },
19439     
19440     setTouchViewValue : function()
19441     {
19442         if(this.multiple){
19443             this.clearItem();
19444         
19445             var _this = this;
19446
19447             Roo.each(this.tickItems, function(o){
19448                 this.addItem(o);
19449             }, this);
19450         }
19451         
19452         this.hideTouchView();
19453     },
19454     
19455     doTouchViewQuery : function()
19456     {
19457         var qe = {
19458             query: '',
19459             forceAll: true,
19460             combo: this,
19461             cancel:false
19462         };
19463         
19464         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19465             return false;
19466         }
19467         
19468         if(!this.alwaysQuery || this.mode == 'local'){
19469             this.onTouchViewLoad();
19470             return;
19471         }
19472         
19473         this.store.load();
19474     },
19475     
19476     onTouchViewBeforeLoad : function(combo,opts)
19477     {
19478         return;
19479     },
19480
19481     // private
19482     onTouchViewLoad : function()
19483     {
19484         if(this.store.getCount() < 1){
19485             this.onTouchViewEmptyResults();
19486             return;
19487         }
19488         
19489         this.clearTouchView();
19490         
19491         var rawValue = this.getRawValue();
19492         
19493         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19494         
19495         this.tickItems = [];
19496         
19497         this.store.data.each(function(d, rowIndex){
19498             var row = this.touchViewListGroup.createChild(template);
19499             
19500             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19501                 row.addClass(d.data.cls);
19502             }
19503             
19504             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19505                 var cfg = {
19506                     data : d.data,
19507                     html : d.data[this.displayField]
19508                 };
19509                 
19510                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19511                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19512                 }
19513             }
19514             row.removeClass('selected');
19515             if(!this.multiple && this.valueField &&
19516                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19517             {
19518                 // radio buttons..
19519                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19520                 row.addClass('selected');
19521             }
19522             
19523             if(this.multiple && this.valueField &&
19524                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19525             {
19526                 
19527                 // checkboxes...
19528                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19529                 this.tickItems.push(d.data);
19530             }
19531             
19532             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19533             
19534         }, this);
19535         
19536         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19537         
19538         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19539
19540         if(this.modalTitle.length){
19541             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19542         }
19543
19544         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19545         
19546         if(this.mobile_restrict_height && listHeight < bodyHeight){
19547             this.touchViewBodyEl.setHeight(listHeight);
19548         }
19549         
19550         var _this = this;
19551         
19552         if(firstChecked && listHeight > bodyHeight){
19553             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19554         }
19555         
19556     },
19557     
19558     onTouchViewLoadException : function()
19559     {
19560         this.hideTouchView();
19561     },
19562     
19563     onTouchViewEmptyResults : function()
19564     {
19565         this.clearTouchView();
19566         
19567         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19568         
19569         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19570         
19571     },
19572     
19573     clearTouchView : function()
19574     {
19575         this.touchViewListGroup.dom.innerHTML = '';
19576     },
19577     
19578     onTouchViewClick : function(e, el, o)
19579     {
19580         e.preventDefault();
19581         
19582         var row = o.row;
19583         var rowIndex = o.rowIndex;
19584         
19585         var r = this.store.getAt(rowIndex);
19586         
19587         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19588             
19589             if(!this.multiple){
19590                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19591                     c.dom.removeAttribute('checked');
19592                 }, this);
19593
19594                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19595
19596                 this.setFromData(r.data);
19597
19598                 var close = this.closeTriggerEl();
19599
19600                 if(close){
19601                     close.show();
19602                 }
19603
19604                 this.hideTouchView();
19605
19606                 this.fireEvent('select', this, r, rowIndex);
19607
19608                 return;
19609             }
19610
19611             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19612                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19613                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19614                 return;
19615             }
19616
19617             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19618             this.addItem(r.data);
19619             this.tickItems.push(r.data);
19620         }
19621     },
19622     
19623     getAutoCreateNativeIOS : function()
19624     {
19625         var cfg = {
19626             cls: 'form-group' //input-group,
19627         };
19628         
19629         var combobox =  {
19630             tag: 'select',
19631             cls : 'roo-ios-select'
19632         };
19633         
19634         if (this.name) {
19635             combobox.name = this.name;
19636         }
19637         
19638         if (this.disabled) {
19639             combobox.disabled = true;
19640         }
19641         
19642         var settings = this;
19643         
19644         ['xs','sm','md','lg'].map(function(size){
19645             if (settings[size]) {
19646                 cfg.cls += ' col-' + size + '-' + settings[size];
19647             }
19648         });
19649         
19650         cfg.cn = combobox;
19651         
19652         return cfg;
19653         
19654     },
19655     
19656     initIOSView : function()
19657     {
19658         this.store.on('load', this.onIOSViewLoad, this);
19659         
19660         return;
19661     },
19662     
19663     onIOSViewLoad : function()
19664     {
19665         if(this.store.getCount() < 1){
19666             return;
19667         }
19668         
19669         this.clearIOSView();
19670         
19671         if(this.allowBlank) {
19672             
19673             var default_text = '-- SELECT --';
19674             
19675             if(this.placeholder.length){
19676                 default_text = this.placeholder;
19677             }
19678             
19679             if(this.emptyTitle.length){
19680                 default_text += ' - ' + this.emptyTitle + ' -';
19681             }
19682             
19683             var opt = this.inputEl().createChild({
19684                 tag: 'option',
19685                 value : 0,
19686                 html : default_text
19687             });
19688             
19689             var o = {};
19690             o[this.valueField] = 0;
19691             o[this.displayField] = default_text;
19692             
19693             this.ios_options.push({
19694                 data : o,
19695                 el : opt
19696             });
19697             
19698         }
19699         
19700         this.store.data.each(function(d, rowIndex){
19701             
19702             var html = '';
19703             
19704             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19705                 html = d.data[this.displayField];
19706             }
19707             
19708             var value = '';
19709             
19710             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19711                 value = d.data[this.valueField];
19712             }
19713             
19714             var option = {
19715                 tag: 'option',
19716                 value : value,
19717                 html : html
19718             };
19719             
19720             if(this.value == d.data[this.valueField]){
19721                 option['selected'] = true;
19722             }
19723             
19724             var opt = this.inputEl().createChild(option);
19725             
19726             this.ios_options.push({
19727                 data : d.data,
19728                 el : opt
19729             });
19730             
19731         }, this);
19732         
19733         this.inputEl().on('change', function(){
19734            this.fireEvent('select', this);
19735         }, this);
19736         
19737     },
19738     
19739     clearIOSView: function()
19740     {
19741         this.inputEl().dom.innerHTML = '';
19742         
19743         this.ios_options = [];
19744     },
19745     
19746     setIOSValue: function(v)
19747     {
19748         this.value = v;
19749         
19750         if(!this.ios_options){
19751             return;
19752         }
19753         
19754         Roo.each(this.ios_options, function(opts){
19755            
19756            opts.el.dom.removeAttribute('selected');
19757            
19758            if(opts.data[this.valueField] != v){
19759                return;
19760            }
19761            
19762            opts.el.dom.setAttribute('selected', true);
19763            
19764         }, this);
19765     }
19766
19767     /** 
19768     * @cfg {Boolean} grow 
19769     * @hide 
19770     */
19771     /** 
19772     * @cfg {Number} growMin 
19773     * @hide 
19774     */
19775     /** 
19776     * @cfg {Number} growMax 
19777     * @hide 
19778     */
19779     /**
19780      * @hide
19781      * @method autoSize
19782      */
19783 });
19784
19785 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19786     
19787     header : {
19788         tag: 'div',
19789         cls: 'modal-header',
19790         cn: [
19791             {
19792                 tag: 'h4',
19793                 cls: 'modal-title'
19794             }
19795         ]
19796     },
19797     
19798     body : {
19799         tag: 'div',
19800         cls: 'modal-body',
19801         cn: [
19802             {
19803                 tag: 'ul',
19804                 cls: 'list-group'
19805             }
19806         ]
19807     },
19808     
19809     listItemRadio : {
19810         tag: 'li',
19811         cls: 'list-group-item',
19812         cn: [
19813             {
19814                 tag: 'span',
19815                 cls: 'roo-combobox-list-group-item-value'
19816             },
19817             {
19818                 tag: 'div',
19819                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19820                 cn: [
19821                     {
19822                         tag: 'input',
19823                         type: 'radio'
19824                     },
19825                     {
19826                         tag: 'label'
19827                     }
19828                 ]
19829             }
19830         ]
19831     },
19832     
19833     listItemCheckbox : {
19834         tag: 'li',
19835         cls: 'list-group-item',
19836         cn: [
19837             {
19838                 tag: 'span',
19839                 cls: 'roo-combobox-list-group-item-value'
19840             },
19841             {
19842                 tag: 'div',
19843                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19844                 cn: [
19845                     {
19846                         tag: 'input',
19847                         type: 'checkbox'
19848                     },
19849                     {
19850                         tag: 'label'
19851                     }
19852                 ]
19853             }
19854         ]
19855     },
19856     
19857     emptyResult : {
19858         tag: 'div',
19859         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19860     },
19861     
19862     footer : {
19863         tag: 'div',
19864         cls: 'modal-footer',
19865         cn: [
19866             {
19867                 tag: 'div',
19868                 cls: 'row',
19869                 cn: [
19870                     {
19871                         tag: 'div',
19872                         cls: 'col-xs-6 text-left',
19873                         cn: {
19874                             tag: 'button',
19875                             cls: 'btn btn-danger roo-touch-view-cancel',
19876                             html: 'Cancel'
19877                         }
19878                     },
19879                     {
19880                         tag: 'div',
19881                         cls: 'col-xs-6 text-right',
19882                         cn: {
19883                             tag: 'button',
19884                             cls: 'btn btn-success roo-touch-view-ok',
19885                             html: 'OK'
19886                         }
19887                     }
19888                 ]
19889             }
19890         ]
19891         
19892     }
19893 });
19894
19895 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19896     
19897     touchViewTemplate : {
19898         tag: 'div',
19899         cls: 'modal fade roo-combobox-touch-view',
19900         cn: [
19901             {
19902                 tag: 'div',
19903                 cls: 'modal-dialog',
19904                 style : 'position:fixed', // we have to fix position....
19905                 cn: [
19906                     {
19907                         tag: 'div',
19908                         cls: 'modal-content',
19909                         cn: [
19910                             Roo.bootstrap.form.ComboBox.header,
19911                             Roo.bootstrap.form.ComboBox.body,
19912                             Roo.bootstrap.form.ComboBox.footer
19913                         ]
19914                     }
19915                 ]
19916             }
19917         ]
19918     }
19919 });/*
19920  * Based on:
19921  * Ext JS Library 1.1.1
19922  * Copyright(c) 2006-2007, Ext JS, LLC.
19923  *
19924  * Originally Released Under LGPL - original licence link has changed is not relivant.
19925  *
19926  * Fork - LGPL
19927  * <script type="text/javascript">
19928  */
19929
19930 /**
19931  * @class Roo.View
19932  * @extends Roo.util.Observable
19933  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19934  * This class also supports single and multi selection modes. <br>
19935  * Create a data model bound view:
19936  <pre><code>
19937  var store = new Roo.data.Store(...);
19938
19939  var view = new Roo.View({
19940     el : "my-element",
19941     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19942  
19943     singleSelect: true,
19944     selectedClass: "ydataview-selected",
19945     store: store
19946  });
19947
19948  // listen for node click?
19949  view.on("click", function(vw, index, node, e){
19950  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19951  });
19952
19953  // load XML data
19954  dataModel.load("foobar.xml");
19955  </code></pre>
19956  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19957  * <br><br>
19958  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19959  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19960  * 
19961  * Note: old style constructor is still suported (container, template, config)
19962  * 
19963  * @constructor
19964  * Create a new View
19965  * @param {Object} config The config object
19966  * 
19967  */
19968 Roo.View = function(config, depreciated_tpl, depreciated_config){
19969     
19970     this.parent = false;
19971     
19972     if (typeof(depreciated_tpl) == 'undefined') {
19973         // new way.. - universal constructor.
19974         Roo.apply(this, config);
19975         this.el  = Roo.get(this.el);
19976     } else {
19977         // old format..
19978         this.el  = Roo.get(config);
19979         this.tpl = depreciated_tpl;
19980         Roo.apply(this, depreciated_config);
19981     }
19982     this.wrapEl  = this.el.wrap().wrap();
19983     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19984     
19985     
19986     if(typeof(this.tpl) == "string"){
19987         this.tpl = new Roo.Template(this.tpl);
19988     } else {
19989         // support xtype ctors..
19990         this.tpl = new Roo.factory(this.tpl, Roo);
19991     }
19992     
19993     
19994     this.tpl.compile();
19995     
19996     /** @private */
19997     this.addEvents({
19998         /**
19999          * @event beforeclick
20000          * Fires before a click is processed. Returns false to cancel the default action.
20001          * @param {Roo.View} this
20002          * @param {Number} index The index of the target node
20003          * @param {HTMLElement} node The target node
20004          * @param {Roo.EventObject} e The raw event object
20005          */
20006             "beforeclick" : true,
20007         /**
20008          * @event click
20009          * Fires when a template node is clicked.
20010          * @param {Roo.View} this
20011          * @param {Number} index The index of the target node
20012          * @param {HTMLElement} node The target node
20013          * @param {Roo.EventObject} e The raw event object
20014          */
20015             "click" : true,
20016         /**
20017          * @event dblclick
20018          * Fires when a template node is double clicked.
20019          * @param {Roo.View} this
20020          * @param {Number} index The index of the target node
20021          * @param {HTMLElement} node The target node
20022          * @param {Roo.EventObject} e The raw event object
20023          */
20024             "dblclick" : true,
20025         /**
20026          * @event contextmenu
20027          * Fires when a template node is right clicked.
20028          * @param {Roo.View} this
20029          * @param {Number} index The index of the target node
20030          * @param {HTMLElement} node The target node
20031          * @param {Roo.EventObject} e The raw event object
20032          */
20033             "contextmenu" : true,
20034         /**
20035          * @event selectionchange
20036          * Fires when the selected nodes change.
20037          * @param {Roo.View} this
20038          * @param {Array} selections Array of the selected nodes
20039          */
20040             "selectionchange" : true,
20041     
20042         /**
20043          * @event beforeselect
20044          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20045          * @param {Roo.View} this
20046          * @param {HTMLElement} node The node to be selected
20047          * @param {Array} selections Array of currently selected nodes
20048          */
20049             "beforeselect" : true,
20050         /**
20051          * @event preparedata
20052          * Fires on every row to render, to allow you to change the data.
20053          * @param {Roo.View} this
20054          * @param {Object} data to be rendered (change this)
20055          */
20056           "preparedata" : true
20057           
20058           
20059         });
20060
20061
20062
20063     this.el.on({
20064         "click": this.onClick,
20065         "dblclick": this.onDblClick,
20066         "contextmenu": this.onContextMenu,
20067         scope:this
20068     });
20069
20070     this.selections = [];
20071     this.nodes = [];
20072     this.cmp = new Roo.CompositeElementLite([]);
20073     if(this.store){
20074         this.store = Roo.factory(this.store, Roo.data);
20075         this.setStore(this.store, true);
20076     }
20077     
20078     if ( this.footer && this.footer.xtype) {
20079            
20080          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20081         
20082         this.footer.dataSource = this.store;
20083         this.footer.container = fctr;
20084         this.footer = Roo.factory(this.footer, Roo);
20085         fctr.insertFirst(this.el);
20086         
20087         // this is a bit insane - as the paging toolbar seems to detach the el..
20088 //        dom.parentNode.parentNode.parentNode
20089          // they get detached?
20090     }
20091     
20092     
20093     Roo.View.superclass.constructor.call(this);
20094     
20095     
20096 };
20097
20098 Roo.extend(Roo.View, Roo.util.Observable, {
20099     
20100      /**
20101      * @cfg {Roo.data.Store} store Data store to load data from.
20102      */
20103     store : false,
20104     
20105     /**
20106      * @cfg {String|Roo.Element} el The container element.
20107      */
20108     el : '',
20109     
20110     /**
20111      * @cfg {String|Roo.Template} tpl The template used by this View 
20112      */
20113     tpl : false,
20114     /**
20115      * @cfg {String} dataName the named area of the template to use as the data area
20116      *                          Works with domtemplates roo-name="name"
20117      */
20118     dataName: false,
20119     /**
20120      * @cfg {String} selectedClass The css class to add to selected nodes
20121      */
20122     selectedClass : "x-view-selected",
20123      /**
20124      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20125      */
20126     emptyText : "",
20127     
20128     /**
20129      * @cfg {String} text to display on mask (default Loading)
20130      */
20131     mask : false,
20132     /**
20133      * @cfg {Boolean} multiSelect Allow multiple selection
20134      */
20135     multiSelect : false,
20136     /**
20137      * @cfg {Boolean} singleSelect Allow single selection
20138      */
20139     singleSelect:  false,
20140     
20141     /**
20142      * @cfg {Boolean} toggleSelect - selecting 
20143      */
20144     toggleSelect : false,
20145     
20146     /**
20147      * @cfg {Boolean} tickable - selecting 
20148      */
20149     tickable : false,
20150     
20151     /**
20152      * Returns the element this view is bound to.
20153      * @return {Roo.Element}
20154      */
20155     getEl : function(){
20156         return this.wrapEl;
20157     },
20158     
20159     
20160
20161     /**
20162      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20163      */
20164     refresh : function(){
20165         //Roo.log('refresh');
20166         var t = this.tpl;
20167         
20168         // if we are using something like 'domtemplate', then
20169         // the what gets used is:
20170         // t.applySubtemplate(NAME, data, wrapping data..)
20171         // the outer template then get' applied with
20172         //     the store 'extra data'
20173         // and the body get's added to the
20174         //      roo-name="data" node?
20175         //      <span class='roo-tpl-{name}'></span> ?????
20176         
20177         
20178         
20179         this.clearSelections();
20180         this.el.update("");
20181         var html = [];
20182         var records = this.store.getRange();
20183         if(records.length < 1) {
20184             
20185             // is this valid??  = should it render a template??
20186             
20187             this.el.update(this.emptyText);
20188             return;
20189         }
20190         var el = this.el;
20191         if (this.dataName) {
20192             this.el.update(t.apply(this.store.meta)); //????
20193             el = this.el.child('.roo-tpl-' + this.dataName);
20194         }
20195         
20196         for(var i = 0, len = records.length; i < len; i++){
20197             var data = this.prepareData(records[i].data, i, records[i]);
20198             this.fireEvent("preparedata", this, data, i, records[i]);
20199             
20200             var d = Roo.apply({}, data);
20201             
20202             if(this.tickable){
20203                 Roo.apply(d, {'roo-id' : Roo.id()});
20204                 
20205                 var _this = this;
20206             
20207                 Roo.each(this.parent.item, function(item){
20208                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20209                         return;
20210                     }
20211                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20212                 });
20213             }
20214             
20215             html[html.length] = Roo.util.Format.trim(
20216                 this.dataName ?
20217                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20218                     t.apply(d)
20219             );
20220         }
20221         
20222         
20223         
20224         el.update(html.join(""));
20225         this.nodes = el.dom.childNodes;
20226         this.updateIndexes(0);
20227     },
20228     
20229
20230     /**
20231      * Function to override to reformat the data that is sent to
20232      * the template for each node.
20233      * DEPRICATED - use the preparedata event handler.
20234      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20235      * a JSON object for an UpdateManager bound view).
20236      */
20237     prepareData : function(data, index, record)
20238     {
20239         this.fireEvent("preparedata", this, data, index, record);
20240         return data;
20241     },
20242
20243     onUpdate : function(ds, record){
20244         // Roo.log('on update');   
20245         this.clearSelections();
20246         var index = this.store.indexOf(record);
20247         var n = this.nodes[index];
20248         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20249         n.parentNode.removeChild(n);
20250         this.updateIndexes(index, index);
20251     },
20252
20253     
20254     
20255 // --------- FIXME     
20256     onAdd : function(ds, records, index)
20257     {
20258         //Roo.log(['on Add', ds, records, index] );        
20259         this.clearSelections();
20260         if(this.nodes.length == 0){
20261             this.refresh();
20262             return;
20263         }
20264         var n = this.nodes[index];
20265         for(var i = 0, len = records.length; i < len; i++){
20266             var d = this.prepareData(records[i].data, i, records[i]);
20267             if(n){
20268                 this.tpl.insertBefore(n, d);
20269             }else{
20270                 
20271                 this.tpl.append(this.el, d);
20272             }
20273         }
20274         this.updateIndexes(index);
20275     },
20276
20277     onRemove : function(ds, record, index){
20278        // Roo.log('onRemove');
20279         this.clearSelections();
20280         var el = this.dataName  ?
20281             this.el.child('.roo-tpl-' + this.dataName) :
20282             this.el; 
20283         
20284         el.dom.removeChild(this.nodes[index]);
20285         this.updateIndexes(index);
20286     },
20287
20288     /**
20289      * Refresh an individual node.
20290      * @param {Number} index
20291      */
20292     refreshNode : function(index){
20293         this.onUpdate(this.store, this.store.getAt(index));
20294     },
20295
20296     updateIndexes : function(startIndex, endIndex){
20297         var ns = this.nodes;
20298         startIndex = startIndex || 0;
20299         endIndex = endIndex || ns.length - 1;
20300         for(var i = startIndex; i <= endIndex; i++){
20301             ns[i].nodeIndex = i;
20302         }
20303     },
20304
20305     /**
20306      * Changes the data store this view uses and refresh the view.
20307      * @param {Store} store
20308      */
20309     setStore : function(store, initial){
20310         if(!initial && this.store){
20311             this.store.un("datachanged", this.refresh);
20312             this.store.un("add", this.onAdd);
20313             this.store.un("remove", this.onRemove);
20314             this.store.un("update", this.onUpdate);
20315             this.store.un("clear", this.refresh);
20316             this.store.un("beforeload", this.onBeforeLoad);
20317             this.store.un("load", this.onLoad);
20318             this.store.un("loadexception", this.onLoad);
20319         }
20320         if(store){
20321           
20322             store.on("datachanged", this.refresh, this);
20323             store.on("add", this.onAdd, this);
20324             store.on("remove", this.onRemove, this);
20325             store.on("update", this.onUpdate, this);
20326             store.on("clear", this.refresh, this);
20327             store.on("beforeload", this.onBeforeLoad, this);
20328             store.on("load", this.onLoad, this);
20329             store.on("loadexception", this.onLoad, this);
20330         }
20331         
20332         if(store){
20333             this.refresh();
20334         }
20335     },
20336     /**
20337      * onbeforeLoad - masks the loading area.
20338      *
20339      */
20340     onBeforeLoad : function(store,opts)
20341     {
20342          //Roo.log('onBeforeLoad');   
20343         if (!opts.add) {
20344             this.el.update("");
20345         }
20346         this.el.mask(this.mask ? this.mask : "Loading" ); 
20347     },
20348     onLoad : function ()
20349     {
20350         this.el.unmask();
20351     },
20352     
20353
20354     /**
20355      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20356      * @param {HTMLElement} node
20357      * @return {HTMLElement} The template node
20358      */
20359     findItemFromChild : function(node){
20360         var el = this.dataName  ?
20361             this.el.child('.roo-tpl-' + this.dataName,true) :
20362             this.el.dom; 
20363         
20364         if(!node || node.parentNode == el){
20365                     return node;
20366             }
20367             var p = node.parentNode;
20368             while(p && p != el){
20369             if(p.parentNode == el){
20370                 return p;
20371             }
20372             p = p.parentNode;
20373         }
20374             return null;
20375     },
20376
20377     /** @ignore */
20378     onClick : function(e){
20379         var item = this.findItemFromChild(e.getTarget());
20380         if(item){
20381             var index = this.indexOf(item);
20382             if(this.onItemClick(item, index, e) !== false){
20383                 this.fireEvent("click", this, index, item, e);
20384             }
20385         }else{
20386             this.clearSelections();
20387         }
20388     },
20389
20390     /** @ignore */
20391     onContextMenu : function(e){
20392         var item = this.findItemFromChild(e.getTarget());
20393         if(item){
20394             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20395         }
20396     },
20397
20398     /** @ignore */
20399     onDblClick : function(e){
20400         var item = this.findItemFromChild(e.getTarget());
20401         if(item){
20402             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20403         }
20404     },
20405
20406     onItemClick : function(item, index, e)
20407     {
20408         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20409             return false;
20410         }
20411         if (this.toggleSelect) {
20412             var m = this.isSelected(item) ? 'unselect' : 'select';
20413             //Roo.log(m);
20414             var _t = this;
20415             _t[m](item, true, false);
20416             return true;
20417         }
20418         if(this.multiSelect || this.singleSelect){
20419             if(this.multiSelect && e.shiftKey && this.lastSelection){
20420                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20421             }else{
20422                 this.select(item, this.multiSelect && e.ctrlKey);
20423                 this.lastSelection = item;
20424             }
20425             
20426             if(!this.tickable){
20427                 e.preventDefault();
20428             }
20429             
20430         }
20431         return true;
20432     },
20433
20434     /**
20435      * Get the number of selected nodes.
20436      * @return {Number}
20437      */
20438     getSelectionCount : function(){
20439         return this.selections.length;
20440     },
20441
20442     /**
20443      * Get the currently selected nodes.
20444      * @return {Array} An array of HTMLElements
20445      */
20446     getSelectedNodes : function(){
20447         return this.selections;
20448     },
20449
20450     /**
20451      * Get the indexes of the selected nodes.
20452      * @return {Array}
20453      */
20454     getSelectedIndexes : function(){
20455         var indexes = [], s = this.selections;
20456         for(var i = 0, len = s.length; i < len; i++){
20457             indexes.push(s[i].nodeIndex);
20458         }
20459         return indexes;
20460     },
20461
20462     /**
20463      * Clear all selections
20464      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20465      */
20466     clearSelections : function(suppressEvent){
20467         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20468             this.cmp.elements = this.selections;
20469             this.cmp.removeClass(this.selectedClass);
20470             this.selections = [];
20471             if(!suppressEvent){
20472                 this.fireEvent("selectionchange", this, this.selections);
20473             }
20474         }
20475     },
20476
20477     /**
20478      * Returns true if the passed node is selected
20479      * @param {HTMLElement/Number} node The node or node index
20480      * @return {Boolean}
20481      */
20482     isSelected : function(node){
20483         var s = this.selections;
20484         if(s.length < 1){
20485             return false;
20486         }
20487         node = this.getNode(node);
20488         return s.indexOf(node) !== -1;
20489     },
20490
20491     /**
20492      * Selects nodes.
20493      * @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
20494      * @param {Boolean} keepExisting (optional) true to keep existing selections
20495      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20496      */
20497     select : function(nodeInfo, keepExisting, suppressEvent){
20498         if(nodeInfo instanceof Array){
20499             if(!keepExisting){
20500                 this.clearSelections(true);
20501             }
20502             for(var i = 0, len = nodeInfo.length; i < len; i++){
20503                 this.select(nodeInfo[i], true, true);
20504             }
20505             return;
20506         } 
20507         var node = this.getNode(nodeInfo);
20508         if(!node || this.isSelected(node)){
20509             return; // already selected.
20510         }
20511         if(!keepExisting){
20512             this.clearSelections(true);
20513         }
20514         
20515         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20516             Roo.fly(node).addClass(this.selectedClass);
20517             this.selections.push(node);
20518             if(!suppressEvent){
20519                 this.fireEvent("selectionchange", this, this.selections);
20520             }
20521         }
20522         
20523         
20524     },
20525       /**
20526      * Unselects nodes.
20527      * @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
20528      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20529      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20530      */
20531     unselect : function(nodeInfo, keepExisting, suppressEvent)
20532     {
20533         if(nodeInfo instanceof Array){
20534             Roo.each(this.selections, function(s) {
20535                 this.unselect(s, nodeInfo);
20536             }, this);
20537             return;
20538         }
20539         var node = this.getNode(nodeInfo);
20540         if(!node || !this.isSelected(node)){
20541             //Roo.log("not selected");
20542             return; // not selected.
20543         }
20544         // fireevent???
20545         var ns = [];
20546         Roo.each(this.selections, function(s) {
20547             if (s == node ) {
20548                 Roo.fly(node).removeClass(this.selectedClass);
20549
20550                 return;
20551             }
20552             ns.push(s);
20553         },this);
20554         
20555         this.selections= ns;
20556         this.fireEvent("selectionchange", this, this.selections);
20557     },
20558
20559     /**
20560      * Gets a template node.
20561      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20562      * @return {HTMLElement} The node or null if it wasn't found
20563      */
20564     getNode : function(nodeInfo){
20565         if(typeof nodeInfo == "string"){
20566             return document.getElementById(nodeInfo);
20567         }else if(typeof nodeInfo == "number"){
20568             return this.nodes[nodeInfo];
20569         }
20570         return nodeInfo;
20571     },
20572
20573     /**
20574      * Gets a range template nodes.
20575      * @param {Number} startIndex
20576      * @param {Number} endIndex
20577      * @return {Array} An array of nodes
20578      */
20579     getNodes : function(start, end){
20580         var ns = this.nodes;
20581         start = start || 0;
20582         end = typeof end == "undefined" ? ns.length - 1 : end;
20583         var nodes = [];
20584         if(start <= end){
20585             for(var i = start; i <= end; i++){
20586                 nodes.push(ns[i]);
20587             }
20588         } else{
20589             for(var i = start; i >= end; i--){
20590                 nodes.push(ns[i]);
20591             }
20592         }
20593         return nodes;
20594     },
20595
20596     /**
20597      * Finds the index of the passed node
20598      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20599      * @return {Number} The index of the node or -1
20600      */
20601     indexOf : function(node){
20602         node = this.getNode(node);
20603         if(typeof node.nodeIndex == "number"){
20604             return node.nodeIndex;
20605         }
20606         var ns = this.nodes;
20607         for(var i = 0, len = ns.length; i < len; i++){
20608             if(ns[i] == node){
20609                 return i;
20610             }
20611         }
20612         return -1;
20613     }
20614 });
20615 /*
20616  * - LGPL
20617  *
20618  * based on jquery fullcalendar
20619  * 
20620  */
20621
20622 Roo.bootstrap = Roo.bootstrap || {};
20623 /**
20624  * @class Roo.bootstrap.Calendar
20625  * @extends Roo.bootstrap.Component
20626  * Bootstrap Calendar class
20627  * @cfg {Boolean} loadMask (true|false) default false
20628  * @cfg {Object} header generate the user specific header of the calendar, default false
20629
20630  * @constructor
20631  * Create a new Container
20632  * @param {Object} config The config object
20633  */
20634
20635
20636
20637 Roo.bootstrap.Calendar = function(config){
20638     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20639      this.addEvents({
20640         /**
20641              * @event select
20642              * Fires when a date is selected
20643              * @param {DatePicker} this
20644              * @param {Date} date The selected date
20645              */
20646         'select': true,
20647         /**
20648              * @event monthchange
20649              * Fires when the displayed month changes 
20650              * @param {DatePicker} this
20651              * @param {Date} date The selected month
20652              */
20653         'monthchange': true,
20654         /**
20655              * @event evententer
20656              * Fires when mouse over an event
20657              * @param {Calendar} this
20658              * @param {event} Event
20659              */
20660         'evententer': true,
20661         /**
20662              * @event eventleave
20663              * Fires when the mouse leaves an
20664              * @param {Calendar} this
20665              * @param {event}
20666              */
20667         'eventleave': true,
20668         /**
20669              * @event eventclick
20670              * Fires when the mouse click an
20671              * @param {Calendar} this
20672              * @param {event}
20673              */
20674         'eventclick': true
20675         
20676     });
20677
20678 };
20679
20680 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20681     
20682           /**
20683      * @cfg {Roo.data.Store} store
20684      * The data source for the calendar
20685      */
20686         store : false,
20687      /**
20688      * @cfg {Number} startDay
20689      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20690      */
20691     startDay : 0,
20692     
20693     loadMask : false,
20694     
20695     header : false,
20696       
20697     getAutoCreate : function(){
20698         
20699         
20700         var fc_button = function(name, corner, style, content ) {
20701             return Roo.apply({},{
20702                 tag : 'span',
20703                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20704                          (corner.length ?
20705                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20706                             ''
20707                         ),
20708                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20709                 unselectable: 'on'
20710             });
20711         };
20712         
20713         var header = {};
20714         
20715         if(!this.header){
20716             header = {
20717                 tag : 'table',
20718                 cls : 'fc-header',
20719                 style : 'width:100%',
20720                 cn : [
20721                     {
20722                         tag: 'tr',
20723                         cn : [
20724                             {
20725                                 tag : 'td',
20726                                 cls : 'fc-header-left',
20727                                 cn : [
20728                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20729                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20730                                     { tag: 'span', cls: 'fc-header-space' },
20731                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20732
20733
20734                                 ]
20735                             },
20736
20737                             {
20738                                 tag : 'td',
20739                                 cls : 'fc-header-center',
20740                                 cn : [
20741                                     {
20742                                         tag: 'span',
20743                                         cls: 'fc-header-title',
20744                                         cn : {
20745                                             tag: 'H2',
20746                                             html : 'month / year'
20747                                         }
20748                                     }
20749
20750                                 ]
20751                             },
20752                             {
20753                                 tag : 'td',
20754                                 cls : 'fc-header-right',
20755                                 cn : [
20756                               /*      fc_button('month', 'left', '', 'month' ),
20757                                     fc_button('week', '', '', 'week' ),
20758                                     fc_button('day', 'right', '', 'day' )
20759                                 */    
20760
20761                                 ]
20762                             }
20763
20764                         ]
20765                     }
20766                 ]
20767             };
20768         }
20769         
20770         header = this.header;
20771         
20772        
20773         var cal_heads = function() {
20774             var ret = [];
20775             // fixme - handle this.
20776             
20777             for (var i =0; i < Date.dayNames.length; i++) {
20778                 var d = Date.dayNames[i];
20779                 ret.push({
20780                     tag: 'th',
20781                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20782                     html : d.substring(0,3)
20783                 });
20784                 
20785             }
20786             ret[0].cls += ' fc-first';
20787             ret[6].cls += ' fc-last';
20788             return ret;
20789         };
20790         var cal_cell = function(n) {
20791             return  {
20792                 tag: 'td',
20793                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20794                 cn : [
20795                     {
20796                         cn : [
20797                             {
20798                                 cls: 'fc-day-number',
20799                                 html: 'D'
20800                             },
20801                             {
20802                                 cls: 'fc-day-content',
20803                              
20804                                 cn : [
20805                                      {
20806                                         style: 'position: relative;' // height: 17px;
20807                                     }
20808                                 ]
20809                             }
20810                             
20811                             
20812                         ]
20813                     }
20814                 ]
20815                 
20816             }
20817         };
20818         var cal_rows = function() {
20819             
20820             var ret = [];
20821             for (var r = 0; r < 6; r++) {
20822                 var row= {
20823                     tag : 'tr',
20824                     cls : 'fc-week',
20825                     cn : []
20826                 };
20827                 
20828                 for (var i =0; i < Date.dayNames.length; i++) {
20829                     var d = Date.dayNames[i];
20830                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20831
20832                 }
20833                 row.cn[0].cls+=' fc-first';
20834                 row.cn[0].cn[0].style = 'min-height:90px';
20835                 row.cn[6].cls+=' fc-last';
20836                 ret.push(row);
20837                 
20838             }
20839             ret[0].cls += ' fc-first';
20840             ret[4].cls += ' fc-prev-last';
20841             ret[5].cls += ' fc-last';
20842             return ret;
20843             
20844         };
20845         
20846         var cal_table = {
20847             tag: 'table',
20848             cls: 'fc-border-separate',
20849             style : 'width:100%',
20850             cellspacing  : 0,
20851             cn : [
20852                 { 
20853                     tag: 'thead',
20854                     cn : [
20855                         { 
20856                             tag: 'tr',
20857                             cls : 'fc-first fc-last',
20858                             cn : cal_heads()
20859                         }
20860                     ]
20861                 },
20862                 { 
20863                     tag: 'tbody',
20864                     cn : cal_rows()
20865                 }
20866                   
20867             ]
20868         };
20869          
20870          var cfg = {
20871             cls : 'fc fc-ltr',
20872             cn : [
20873                 header,
20874                 {
20875                     cls : 'fc-content',
20876                     style : "position: relative;",
20877                     cn : [
20878                         {
20879                             cls : 'fc-view fc-view-month fc-grid',
20880                             style : 'position: relative',
20881                             unselectable : 'on',
20882                             cn : [
20883                                 {
20884                                     cls : 'fc-event-container',
20885                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20886                                 },
20887                                 cal_table
20888                             ]
20889                         }
20890                     ]
20891     
20892                 }
20893            ] 
20894             
20895         };
20896         
20897          
20898         
20899         return cfg;
20900     },
20901     
20902     
20903     initEvents : function()
20904     {
20905         if(!this.store){
20906             throw "can not find store for calendar";
20907         }
20908         
20909         var mark = {
20910             tag: "div",
20911             cls:"x-dlg-mask",
20912             style: "text-align:center",
20913             cn: [
20914                 {
20915                     tag: "div",
20916                     style: "background-color:white;width:50%;margin:250 auto",
20917                     cn: [
20918                         {
20919                             tag: "img",
20920                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20921                         },
20922                         {
20923                             tag: "span",
20924                             html: "Loading"
20925                         }
20926                         
20927                     ]
20928                 }
20929             ]
20930         };
20931         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20932         
20933         var size = this.el.select('.fc-content', true).first().getSize();
20934         this.maskEl.setSize(size.width, size.height);
20935         this.maskEl.enableDisplayMode("block");
20936         if(!this.loadMask){
20937             this.maskEl.hide();
20938         }
20939         
20940         this.store = Roo.factory(this.store, Roo.data);
20941         this.store.on('load', this.onLoad, this);
20942         this.store.on('beforeload', this.onBeforeLoad, this);
20943         
20944         this.resize();
20945         
20946         this.cells = this.el.select('.fc-day',true);
20947         //Roo.log(this.cells);
20948         this.textNodes = this.el.query('.fc-day-number');
20949         this.cells.addClassOnOver('fc-state-hover');
20950         
20951         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20952         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20953         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20954         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20955         
20956         this.on('monthchange', this.onMonthChange, this);
20957         
20958         this.update(new Date().clearTime());
20959     },
20960     
20961     resize : function() {
20962         var sz  = this.el.getSize();
20963         
20964         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20965         this.el.select('.fc-day-content div',true).setHeight(34);
20966     },
20967     
20968     
20969     // private
20970     showPrevMonth : function(e){
20971         this.update(this.activeDate.add("mo", -1));
20972     },
20973     showToday : function(e){
20974         this.update(new Date().clearTime());
20975     },
20976     // private
20977     showNextMonth : function(e){
20978         this.update(this.activeDate.add("mo", 1));
20979     },
20980
20981     // private
20982     showPrevYear : function(){
20983         this.update(this.activeDate.add("y", -1));
20984     },
20985
20986     // private
20987     showNextYear : function(){
20988         this.update(this.activeDate.add("y", 1));
20989     },
20990
20991     
20992    // private
20993     update : function(date)
20994     {
20995         var vd = this.activeDate;
20996         this.activeDate = date;
20997 //        if(vd && this.el){
20998 //            var t = date.getTime();
20999 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21000 //                Roo.log('using add remove');
21001 //                
21002 //                this.fireEvent('monthchange', this, date);
21003 //                
21004 //                this.cells.removeClass("fc-state-highlight");
21005 //                this.cells.each(function(c){
21006 //                   if(c.dateValue == t){
21007 //                       c.addClass("fc-state-highlight");
21008 //                       setTimeout(function(){
21009 //                            try{c.dom.firstChild.focus();}catch(e){}
21010 //                       }, 50);
21011 //                       return false;
21012 //                   }
21013 //                   return true;
21014 //                });
21015 //                return;
21016 //            }
21017 //        }
21018         
21019         var days = date.getDaysInMonth();
21020         
21021         var firstOfMonth = date.getFirstDateOfMonth();
21022         var startingPos = firstOfMonth.getDay()-this.startDay;
21023         
21024         if(startingPos < this.startDay){
21025             startingPos += 7;
21026         }
21027         
21028         var pm = date.add(Date.MONTH, -1);
21029         var prevStart = pm.getDaysInMonth()-startingPos;
21030 //        
21031         this.cells = this.el.select('.fc-day',true);
21032         this.textNodes = this.el.query('.fc-day-number');
21033         this.cells.addClassOnOver('fc-state-hover');
21034         
21035         var cells = this.cells.elements;
21036         var textEls = this.textNodes;
21037         
21038         Roo.each(cells, function(cell){
21039             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21040         });
21041         
21042         days += startingPos;
21043
21044         // convert everything to numbers so it's fast
21045         var day = 86400000;
21046         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21047         //Roo.log(d);
21048         //Roo.log(pm);
21049         //Roo.log(prevStart);
21050         
21051         var today = new Date().clearTime().getTime();
21052         var sel = date.clearTime().getTime();
21053         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21054         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21055         var ddMatch = this.disabledDatesRE;
21056         var ddText = this.disabledDatesText;
21057         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21058         var ddaysText = this.disabledDaysText;
21059         var format = this.format;
21060         
21061         var setCellClass = function(cal, cell){
21062             cell.row = 0;
21063             cell.events = [];
21064             cell.more = [];
21065             //Roo.log('set Cell Class');
21066             cell.title = "";
21067             var t = d.getTime();
21068             
21069             //Roo.log(d);
21070             
21071             cell.dateValue = t;
21072             if(t == today){
21073                 cell.className += " fc-today";
21074                 cell.className += " fc-state-highlight";
21075                 cell.title = cal.todayText;
21076             }
21077             if(t == sel){
21078                 // disable highlight in other month..
21079                 //cell.className += " fc-state-highlight";
21080                 
21081             }
21082             // disabling
21083             if(t < min) {
21084                 cell.className = " fc-state-disabled";
21085                 cell.title = cal.minText;
21086                 return;
21087             }
21088             if(t > max) {
21089                 cell.className = " fc-state-disabled";
21090                 cell.title = cal.maxText;
21091                 return;
21092             }
21093             if(ddays){
21094                 if(ddays.indexOf(d.getDay()) != -1){
21095                     cell.title = ddaysText;
21096                     cell.className = " fc-state-disabled";
21097                 }
21098             }
21099             if(ddMatch && format){
21100                 var fvalue = d.dateFormat(format);
21101                 if(ddMatch.test(fvalue)){
21102                     cell.title = ddText.replace("%0", fvalue);
21103                     cell.className = " fc-state-disabled";
21104                 }
21105             }
21106             
21107             if (!cell.initialClassName) {
21108                 cell.initialClassName = cell.dom.className;
21109             }
21110             
21111             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21112         };
21113
21114         var i = 0;
21115         
21116         for(; i < startingPos; i++) {
21117             textEls[i].innerHTML = (++prevStart);
21118             d.setDate(d.getDate()+1);
21119             
21120             cells[i].className = "fc-past fc-other-month";
21121             setCellClass(this, cells[i]);
21122         }
21123         
21124         var intDay = 0;
21125         
21126         for(; i < days; i++){
21127             intDay = i - startingPos + 1;
21128             textEls[i].innerHTML = (intDay);
21129             d.setDate(d.getDate()+1);
21130             
21131             cells[i].className = ''; // "x-date-active";
21132             setCellClass(this, cells[i]);
21133         }
21134         var extraDays = 0;
21135         
21136         for(; i < 42; i++) {
21137             textEls[i].innerHTML = (++extraDays);
21138             d.setDate(d.getDate()+1);
21139             
21140             cells[i].className = "fc-future fc-other-month";
21141             setCellClass(this, cells[i]);
21142         }
21143         
21144         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21145         
21146         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21147         
21148         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21149         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21150         
21151         if(totalRows != 6){
21152             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21153             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21154         }
21155         
21156         this.fireEvent('monthchange', this, date);
21157         
21158         
21159         /*
21160         if(!this.internalRender){
21161             var main = this.el.dom.firstChild;
21162             var w = main.offsetWidth;
21163             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21164             Roo.fly(main).setWidth(w);
21165             this.internalRender = true;
21166             // opera does not respect the auto grow header center column
21167             // then, after it gets a width opera refuses to recalculate
21168             // without a second pass
21169             if(Roo.isOpera && !this.secondPass){
21170                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21171                 this.secondPass = true;
21172                 this.update.defer(10, this, [date]);
21173             }
21174         }
21175         */
21176         
21177     },
21178     
21179     findCell : function(dt) {
21180         dt = dt.clearTime().getTime();
21181         var ret = false;
21182         this.cells.each(function(c){
21183             //Roo.log("check " +c.dateValue + '?=' + dt);
21184             if(c.dateValue == dt){
21185                 ret = c;
21186                 return false;
21187             }
21188             return true;
21189         });
21190         
21191         return ret;
21192     },
21193     
21194     findCells : function(ev) {
21195         var s = ev.start.clone().clearTime().getTime();
21196        // Roo.log(s);
21197         var e= ev.end.clone().clearTime().getTime();
21198        // Roo.log(e);
21199         var ret = [];
21200         this.cells.each(function(c){
21201              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21202             
21203             if(c.dateValue > e){
21204                 return ;
21205             }
21206             if(c.dateValue < s){
21207                 return ;
21208             }
21209             ret.push(c);
21210         });
21211         
21212         return ret;    
21213     },
21214     
21215 //    findBestRow: function(cells)
21216 //    {
21217 //        var ret = 0;
21218 //        
21219 //        for (var i =0 ; i < cells.length;i++) {
21220 //            ret  = Math.max(cells[i].rows || 0,ret);
21221 //        }
21222 //        return ret;
21223 //        
21224 //    },
21225     
21226     
21227     addItem : function(ev)
21228     {
21229         // look for vertical location slot in
21230         var cells = this.findCells(ev);
21231         
21232 //        ev.row = this.findBestRow(cells);
21233         
21234         // work out the location.
21235         
21236         var crow = false;
21237         var rows = [];
21238         for(var i =0; i < cells.length; i++) {
21239             
21240             cells[i].row = cells[0].row;
21241             
21242             if(i == 0){
21243                 cells[i].row = cells[i].row + 1;
21244             }
21245             
21246             if (!crow) {
21247                 crow = {
21248                     start : cells[i],
21249                     end :  cells[i]
21250                 };
21251                 continue;
21252             }
21253             if (crow.start.getY() == cells[i].getY()) {
21254                 // on same row.
21255                 crow.end = cells[i];
21256                 continue;
21257             }
21258             // different row.
21259             rows.push(crow);
21260             crow = {
21261                 start: cells[i],
21262                 end : cells[i]
21263             };
21264             
21265         }
21266         
21267         rows.push(crow);
21268         ev.els = [];
21269         ev.rows = rows;
21270         ev.cells = cells;
21271         
21272         cells[0].events.push(ev);
21273         
21274         this.calevents.push(ev);
21275     },
21276     
21277     clearEvents: function() {
21278         
21279         if(!this.calevents){
21280             return;
21281         }
21282         
21283         Roo.each(this.cells.elements, function(c){
21284             c.row = 0;
21285             c.events = [];
21286             c.more = [];
21287         });
21288         
21289         Roo.each(this.calevents, function(e) {
21290             Roo.each(e.els, function(el) {
21291                 el.un('mouseenter' ,this.onEventEnter, this);
21292                 el.un('mouseleave' ,this.onEventLeave, this);
21293                 el.remove();
21294             },this);
21295         },this);
21296         
21297         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21298             e.remove();
21299         });
21300         
21301     },
21302     
21303     renderEvents: function()
21304     {   
21305         var _this = this;
21306         
21307         this.cells.each(function(c) {
21308             
21309             if(c.row < 5){
21310                 return;
21311             }
21312             
21313             var ev = c.events;
21314             
21315             var r = 4;
21316             if(c.row != c.events.length){
21317                 r = 4 - (4 - (c.row - c.events.length));
21318             }
21319             
21320             c.events = ev.slice(0, r);
21321             c.more = ev.slice(r);
21322             
21323             if(c.more.length && c.more.length == 1){
21324                 c.events.push(c.more.pop());
21325             }
21326             
21327             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21328             
21329         });
21330             
21331         this.cells.each(function(c) {
21332             
21333             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21334             
21335             
21336             for (var e = 0; e < c.events.length; e++){
21337                 var ev = c.events[e];
21338                 var rows = ev.rows;
21339                 
21340                 for(var i = 0; i < rows.length; i++) {
21341                 
21342                     // how many rows should it span..
21343
21344                     var  cfg = {
21345                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21346                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21347
21348                         unselectable : "on",
21349                         cn : [
21350                             {
21351                                 cls: 'fc-event-inner',
21352                                 cn : [
21353     //                                {
21354     //                                  tag:'span',
21355     //                                  cls: 'fc-event-time',
21356     //                                  html : cells.length > 1 ? '' : ev.time
21357     //                                },
21358                                     {
21359                                       tag:'span',
21360                                       cls: 'fc-event-title',
21361                                       html : String.format('{0}', ev.title)
21362                                     }
21363
21364
21365                                 ]
21366                             },
21367                             {
21368                                 cls: 'ui-resizable-handle ui-resizable-e',
21369                                 html : '&nbsp;&nbsp;&nbsp'
21370                             }
21371
21372                         ]
21373                     };
21374
21375                     if (i == 0) {
21376                         cfg.cls += ' fc-event-start';
21377                     }
21378                     if ((i+1) == rows.length) {
21379                         cfg.cls += ' fc-event-end';
21380                     }
21381
21382                     var ctr = _this.el.select('.fc-event-container',true).first();
21383                     var cg = ctr.createChild(cfg);
21384
21385                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21386                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21387
21388                     var r = (c.more.length) ? 1 : 0;
21389                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21390                     cg.setWidth(ebox.right - sbox.x -2);
21391
21392                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21393                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21394                     cg.on('click', _this.onEventClick, _this, ev);
21395
21396                     ev.els.push(cg);
21397                     
21398                 }
21399                 
21400             }
21401             
21402             
21403             if(c.more.length){
21404                 var  cfg = {
21405                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21406                     style : 'position: absolute',
21407                     unselectable : "on",
21408                     cn : [
21409                         {
21410                             cls: 'fc-event-inner',
21411                             cn : [
21412                                 {
21413                                   tag:'span',
21414                                   cls: 'fc-event-title',
21415                                   html : 'More'
21416                                 }
21417
21418
21419                             ]
21420                         },
21421                         {
21422                             cls: 'ui-resizable-handle ui-resizable-e',
21423                             html : '&nbsp;&nbsp;&nbsp'
21424                         }
21425
21426                     ]
21427                 };
21428
21429                 var ctr = _this.el.select('.fc-event-container',true).first();
21430                 var cg = ctr.createChild(cfg);
21431
21432                 var sbox = c.select('.fc-day-content',true).first().getBox();
21433                 var ebox = c.select('.fc-day-content',true).first().getBox();
21434                 //Roo.log(cg);
21435                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21436                 cg.setWidth(ebox.right - sbox.x -2);
21437
21438                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21439                 
21440             }
21441             
21442         });
21443         
21444         
21445         
21446     },
21447     
21448     onEventEnter: function (e, el,event,d) {
21449         this.fireEvent('evententer', this, el, event);
21450     },
21451     
21452     onEventLeave: function (e, el,event,d) {
21453         this.fireEvent('eventleave', this, el, event);
21454     },
21455     
21456     onEventClick: function (e, el,event,d) {
21457         this.fireEvent('eventclick', this, el, event);
21458     },
21459     
21460     onMonthChange: function () {
21461         this.store.load();
21462     },
21463     
21464     onMoreEventClick: function(e, el, more)
21465     {
21466         var _this = this;
21467         
21468         this.calpopover.placement = 'right';
21469         this.calpopover.setTitle('More');
21470         
21471         this.calpopover.setContent('');
21472         
21473         var ctr = this.calpopover.el.select('.popover-content', true).first();
21474         
21475         Roo.each(more, function(m){
21476             var cfg = {
21477                 cls : 'fc-event-hori fc-event-draggable',
21478                 html : m.title
21479             };
21480             var cg = ctr.createChild(cfg);
21481             
21482             cg.on('click', _this.onEventClick, _this, m);
21483         });
21484         
21485         this.calpopover.show(el);
21486         
21487         
21488     },
21489     
21490     onLoad: function () 
21491     {   
21492         this.calevents = [];
21493         var cal = this;
21494         
21495         if(this.store.getCount() > 0){
21496             this.store.data.each(function(d){
21497                cal.addItem({
21498                     id : d.data.id,
21499                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21500                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21501                     time : d.data.start_time,
21502                     title : d.data.title,
21503                     description : d.data.description,
21504                     venue : d.data.venue
21505                 });
21506             });
21507         }
21508         
21509         this.renderEvents();
21510         
21511         if(this.calevents.length && this.loadMask){
21512             this.maskEl.hide();
21513         }
21514     },
21515     
21516     onBeforeLoad: function()
21517     {
21518         this.clearEvents();
21519         if(this.loadMask){
21520             this.maskEl.show();
21521         }
21522     }
21523 });
21524
21525  
21526  /*
21527  * - LGPL
21528  *
21529  * element
21530  * 
21531  */
21532
21533 /**
21534  * @class Roo.bootstrap.Popover
21535  * @extends Roo.bootstrap.Component
21536  * @parent none builder
21537  * @children Roo.bootstrap.Component
21538  * Bootstrap Popover class
21539  * @cfg {String} html contents of the popover   (or false to use children..)
21540  * @cfg {String} title of popover (or false to hide)
21541  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21542  * @cfg {String} trigger click || hover (or false to trigger manually)
21543  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21544  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21545  *      - if false and it has a 'parent' then it will be automatically added to that element
21546  *      - if string - Roo.get  will be called 
21547  * @cfg {Number} delay - delay before showing
21548  
21549  * @constructor
21550  * Create a new Popover
21551  * @param {Object} config The config object
21552  */
21553
21554 Roo.bootstrap.Popover = function(config){
21555     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21556     
21557     this.addEvents({
21558         // raw events
21559          /**
21560          * @event show
21561          * After the popover show
21562          * 
21563          * @param {Roo.bootstrap.Popover} this
21564          */
21565         "show" : true,
21566         /**
21567          * @event hide
21568          * After the popover hide
21569          * 
21570          * @param {Roo.bootstrap.Popover} this
21571          */
21572         "hide" : true
21573     });
21574 };
21575
21576 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21577     
21578     title: false,
21579     html: false,
21580     
21581     placement : 'right',
21582     trigger : 'hover', // hover
21583     modal : false,
21584     delay : 0,
21585     
21586     over: false,
21587     
21588     can_build_overlaid : false,
21589     
21590     maskEl : false, // the mask element
21591     headerEl : false,
21592     contentEl : false,
21593     alignEl : false, // when show is called with an element - this get's stored.
21594     
21595     getChildContainer : function()
21596     {
21597         return this.contentEl;
21598         
21599     },
21600     getPopoverHeader : function()
21601     {
21602         this.title = true; // flag not to hide it..
21603         this.headerEl.addClass('p-0');
21604         return this.headerEl
21605     },
21606     
21607     
21608     getAutoCreate : function(){
21609          
21610         var cfg = {
21611            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21612            style: 'display:block',
21613            cn : [
21614                 {
21615                     cls : 'arrow'
21616                 },
21617                 {
21618                     cls : 'popover-inner ',
21619                     cn : [
21620                         {
21621                             tag: 'h3',
21622                             cls: 'popover-title popover-header',
21623                             html : this.title === false ? '' : this.title
21624                         },
21625                         {
21626                             cls : 'popover-content popover-body '  + (this.cls || ''),
21627                             html : this.html || ''
21628                         }
21629                     ]
21630                     
21631                 }
21632            ]
21633         };
21634         
21635         return cfg;
21636     },
21637     /**
21638      * @param {string} the title
21639      */
21640     setTitle: function(str)
21641     {
21642         this.title = str;
21643         if (this.el) {
21644             this.headerEl.dom.innerHTML = str;
21645         }
21646         
21647     },
21648     /**
21649      * @param {string} the body content
21650      */
21651     setContent: function(str)
21652     {
21653         this.html = str;
21654         if (this.contentEl) {
21655             this.contentEl.dom.innerHTML = str;
21656         }
21657         
21658     },
21659     // as it get's added to the bottom of the page.
21660     onRender : function(ct, position)
21661     {
21662         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21663         
21664         
21665         
21666         if(!this.el){
21667             var cfg = Roo.apply({},  this.getAutoCreate());
21668             cfg.id = Roo.id();
21669             
21670             if (this.cls) {
21671                 cfg.cls += ' ' + this.cls;
21672             }
21673             if (this.style) {
21674                 cfg.style = this.style;
21675             }
21676             //Roo.log("adding to ");
21677             this.el = Roo.get(document.body).createChild(cfg, position);
21678 //            Roo.log(this.el);
21679         }
21680         
21681         this.contentEl = this.el.select('.popover-content',true).first();
21682         this.headerEl =  this.el.select('.popover-title',true).first();
21683         
21684         var nitems = [];
21685         if(typeof(this.items) != 'undefined'){
21686             var items = this.items;
21687             delete this.items;
21688
21689             for(var i =0;i < items.length;i++) {
21690                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21691             }
21692         }
21693
21694         this.items = nitems;
21695         
21696         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21697         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21698         
21699         
21700         
21701         this.initEvents();
21702     },
21703     
21704     resizeMask : function()
21705     {
21706         this.maskEl.setSize(
21707             Roo.lib.Dom.getViewWidth(true),
21708             Roo.lib.Dom.getViewHeight(true)
21709         );
21710     },
21711     
21712     initEvents : function()
21713     {
21714         
21715         if (!this.modal) { 
21716             Roo.bootstrap.Popover.register(this);
21717         }
21718          
21719         this.arrowEl = this.el.select('.arrow',true).first();
21720         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21721         this.el.enableDisplayMode('block');
21722         this.el.hide();
21723  
21724         
21725         if (this.over === false && !this.parent()) {
21726             return; 
21727         }
21728         if (this.triggers === false) {
21729             return;
21730         }
21731          
21732         // support parent
21733         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21734         var triggers = this.trigger ? this.trigger.split(' ') : [];
21735         Roo.each(triggers, function(trigger) {
21736         
21737             if (trigger == 'click') {
21738                 on_el.on('click', this.toggle, this);
21739             } else if (trigger != 'manual') {
21740                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21741                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21742       
21743                 on_el.on(eventIn  ,this.enter, this);
21744                 on_el.on(eventOut, this.leave, this);
21745             }
21746         }, this);
21747     },
21748     
21749     
21750     // private
21751     timeout : null,
21752     hoverState : null,
21753     
21754     toggle : function () {
21755         this.hoverState == 'in' ? this.leave() : this.enter();
21756     },
21757     
21758     enter : function () {
21759         
21760         clearTimeout(this.timeout);
21761     
21762         this.hoverState = 'in';
21763     
21764         if (!this.delay || !this.delay.show) {
21765             this.show();
21766             return;
21767         }
21768         var _t = this;
21769         this.timeout = setTimeout(function () {
21770             if (_t.hoverState == 'in') {
21771                 _t.show();
21772             }
21773         }, this.delay.show)
21774     },
21775     
21776     leave : function() {
21777         clearTimeout(this.timeout);
21778     
21779         this.hoverState = 'out';
21780     
21781         if (!this.delay || !this.delay.hide) {
21782             this.hide();
21783             return;
21784         }
21785         var _t = this;
21786         this.timeout = setTimeout(function () {
21787             if (_t.hoverState == 'out') {
21788                 _t.hide();
21789             }
21790         }, this.delay.hide)
21791     },
21792     
21793     /**
21794      * update the position of the dialog
21795      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21796      * 
21797      *
21798      */
21799     
21800     doAlign : function()
21801     {
21802         
21803         if (this.alignEl) {
21804             this.updatePosition(this.placement, true);
21805              
21806         } else {
21807             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21808             var es = this.el.getSize();
21809             var x = Roo.lib.Dom.getViewWidth()/2;
21810             var y = Roo.lib.Dom.getViewHeight()/2;
21811             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21812             
21813         }
21814
21815          
21816          
21817         
21818         
21819     },
21820     
21821     /**
21822      * Show the popover
21823      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21824      * @param {string} (left|right|top|bottom) position
21825      */
21826     show : function (on_el, placement)
21827     {
21828         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21829         on_el = on_el || false; // default to false
21830          
21831         if (!on_el) {
21832             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21833                 on_el = this.parent().el;
21834             } else if (this.over) {
21835                 on_el = Roo.get(this.over);
21836             }
21837             
21838         }
21839         
21840         this.alignEl = Roo.get( on_el );
21841
21842         if (!this.el) {
21843             this.render(document.body);
21844         }
21845         
21846         
21847          
21848         
21849         if (this.title === false) {
21850             this.headerEl.hide();
21851         }
21852         
21853        
21854         this.el.show();
21855         this.el.dom.style.display = 'block';
21856          
21857         this.doAlign();
21858         
21859         //var arrow = this.el.select('.arrow',true).first();
21860         //arrow.set(align[2], 
21861         
21862         this.el.addClass('in');
21863         
21864          
21865         
21866         this.hoverState = 'in';
21867         
21868         if (this.modal) {
21869             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21870             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21871             this.maskEl.dom.style.display = 'block';
21872             this.maskEl.addClass('show');
21873         }
21874         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21875  
21876         this.fireEvent('show', this);
21877         
21878     },
21879     /**
21880      * fire this manually after loading a grid in the table for example
21881      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21882      * @param {Boolean} try and move it if we cant get right position.
21883      */
21884     updatePosition : function(placement, try_move)
21885     {
21886         // allow for calling with no parameters
21887         placement = placement   ? placement :  this.placement;
21888         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21889         
21890         this.el.removeClass([
21891             'fade','top','bottom', 'left', 'right','in',
21892             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21893         ]);
21894         this.el.addClass(placement + ' bs-popover-' + placement);
21895         
21896         if (!this.alignEl ) {
21897             return false;
21898         }
21899         
21900         switch (placement) {
21901             case 'right':
21902                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21903                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21904                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21905                     //normal display... or moved up/down.
21906                     this.el.setXY(offset);
21907                     var xy = this.alignEl.getAnchorXY('tr', false);
21908                     xy[0]+=2;xy[1]+=5;
21909                     this.arrowEl.setXY(xy);
21910                     return true;
21911                 }
21912                 // continue through...
21913                 return this.updatePosition('left', false);
21914                 
21915             
21916             case 'left':
21917                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21918                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21919                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21920                     //normal display... or moved up/down.
21921                     this.el.setXY(offset);
21922                     var xy = this.alignEl.getAnchorXY('tl', false);
21923                     xy[0]-=10;xy[1]+=5; // << fix me
21924                     this.arrowEl.setXY(xy);
21925                     return true;
21926                 }
21927                 // call self...
21928                 return this.updatePosition('right', false);
21929             
21930             case 'top':
21931                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21932                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21933                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21934                     //normal display... or moved up/down.
21935                     this.el.setXY(offset);
21936                     var xy = this.alignEl.getAnchorXY('t', false);
21937                     xy[1]-=10; // << fix me
21938                     this.arrowEl.setXY(xy);
21939                     return true;
21940                 }
21941                 // fall through
21942                return this.updatePosition('bottom', false);
21943             
21944             case 'bottom':
21945                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21946                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21947                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21948                     //normal display... or moved up/down.
21949                     this.el.setXY(offset);
21950                     var xy = this.alignEl.getAnchorXY('b', false);
21951                      xy[1]+=2; // << fix me
21952                     this.arrowEl.setXY(xy);
21953                     return true;
21954                 }
21955                 // fall through
21956                 return this.updatePosition('top', false);
21957                 
21958             
21959         }
21960         
21961         
21962         return false;
21963     },
21964     
21965     hide : function()
21966     {
21967         this.el.setXY([0,0]);
21968         this.el.removeClass('in');
21969         this.el.hide();
21970         this.hoverState = null;
21971         this.maskEl.hide(); // always..
21972         this.fireEvent('hide', this);
21973     }
21974     
21975 });
21976
21977
21978 Roo.apply(Roo.bootstrap.Popover, {
21979
21980     alignment : {
21981         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21982         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21983         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21984         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21985     },
21986     
21987     zIndex : 20001,
21988
21989     clickHander : false,
21990     
21991     
21992
21993     onMouseDown : function(e)
21994     {
21995         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21996             /// what is nothing is showing..
21997             this.hideAll();
21998         }
21999          
22000     },
22001     
22002     
22003     popups : [],
22004     
22005     register : function(popup)
22006     {
22007         if (!Roo.bootstrap.Popover.clickHandler) {
22008             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22009         }
22010         // hide other popups.
22011         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22012         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22013         this.hideAll(); //<< why?
22014         //this.popups.push(popup);
22015     },
22016     hideAll : function()
22017     {
22018         this.popups.forEach(function(p) {
22019             p.hide();
22020         });
22021     },
22022     onShow : function() {
22023         Roo.bootstrap.Popover.popups.push(this);
22024     },
22025     onHide : function() {
22026         Roo.bootstrap.Popover.popups.remove(this);
22027     } 
22028
22029 });
22030 /**
22031  * @class Roo.bootstrap.PopoverNav
22032  * @extends Roo.bootstrap.nav.Simplebar
22033  * @parent Roo.bootstrap.Popover
22034  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22035  * @licence LGPL
22036  * Bootstrap Popover header navigation class
22037  * FIXME? should this go under nav?
22038  *
22039  * 
22040  * @constructor
22041  * Create a new Popover Header Navigation 
22042  * @param {Object} config The config object
22043  */
22044
22045 Roo.bootstrap.PopoverNav = function(config){
22046     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22047 };
22048
22049 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22050     
22051     
22052     container_method : 'getPopoverHeader' 
22053     
22054      
22055     
22056     
22057    
22058 });
22059
22060  
22061
22062  /*
22063  * - LGPL
22064  *
22065  * Progress
22066  * 
22067  */
22068
22069 /**
22070  * @class Roo.bootstrap.Progress
22071  * @extends Roo.bootstrap.Component
22072  * @children Roo.bootstrap.ProgressBar
22073  * Bootstrap Progress class
22074  * @cfg {Boolean} striped striped of the progress bar
22075  * @cfg {Boolean} active animated of the progress bar
22076  * 
22077  * 
22078  * @constructor
22079  * Create a new Progress
22080  * @param {Object} config The config object
22081  */
22082
22083 Roo.bootstrap.Progress = function(config){
22084     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22085 };
22086
22087 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22088     
22089     striped : false,
22090     active: false,
22091     
22092     getAutoCreate : function(){
22093         var cfg = {
22094             tag: 'div',
22095             cls: 'progress'
22096         };
22097         
22098         
22099         if(this.striped){
22100             cfg.cls += ' progress-striped';
22101         }
22102       
22103         if(this.active){
22104             cfg.cls += ' active';
22105         }
22106         
22107         
22108         return cfg;
22109     }
22110    
22111 });
22112
22113  
22114
22115  /*
22116  * - LGPL
22117  *
22118  * ProgressBar
22119  * 
22120  */
22121
22122 /**
22123  * @class Roo.bootstrap.ProgressBar
22124  * @extends Roo.bootstrap.Component
22125  * Bootstrap ProgressBar class
22126  * @cfg {Number} aria_valuenow aria-value now
22127  * @cfg {Number} aria_valuemin aria-value min
22128  * @cfg {Number} aria_valuemax aria-value max
22129  * @cfg {String} label label for the progress bar
22130  * @cfg {String} panel (success | info | warning | danger )
22131  * @cfg {String} role role of the progress bar
22132  * @cfg {String} sr_only text
22133  * 
22134  * 
22135  * @constructor
22136  * Create a new ProgressBar
22137  * @param {Object} config The config object
22138  */
22139
22140 Roo.bootstrap.ProgressBar = function(config){
22141     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22142 };
22143
22144 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22145     
22146     aria_valuenow : 0,
22147     aria_valuemin : 0,
22148     aria_valuemax : 100,
22149     label : false,
22150     panel : false,
22151     role : false,
22152     sr_only: false,
22153     
22154     getAutoCreate : function()
22155     {
22156         
22157         var cfg = {
22158             tag: 'div',
22159             cls: 'progress-bar',
22160             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22161         };
22162         
22163         if(this.sr_only){
22164             cfg.cn = {
22165                 tag: 'span',
22166                 cls: 'sr-only',
22167                 html: this.sr_only
22168             }
22169         }
22170         
22171         if(this.role){
22172             cfg.role = this.role;
22173         }
22174         
22175         if(this.aria_valuenow){
22176             cfg['aria-valuenow'] = this.aria_valuenow;
22177         }
22178         
22179         if(this.aria_valuemin){
22180             cfg['aria-valuemin'] = this.aria_valuemin;
22181         }
22182         
22183         if(this.aria_valuemax){
22184             cfg['aria-valuemax'] = this.aria_valuemax;
22185         }
22186         
22187         if(this.label && !this.sr_only){
22188             cfg.html = this.label;
22189         }
22190         
22191         if(this.panel){
22192             cfg.cls += ' progress-bar-' + this.panel;
22193         }
22194         
22195         return cfg;
22196     },
22197     
22198     update : function(aria_valuenow)
22199     {
22200         this.aria_valuenow = aria_valuenow;
22201         
22202         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22203     }
22204    
22205 });
22206
22207  
22208
22209  /**
22210  * @class Roo.bootstrap.TabGroup
22211  * @extends Roo.bootstrap.Column
22212  * @children Roo.bootstrap.TabPanel
22213  * Bootstrap Column class
22214  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22215  * @cfg {Boolean} carousel true to make the group behave like a carousel
22216  * @cfg {Boolean} bullets show bullets for the panels
22217  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22218  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22219  * @cfg {Boolean} showarrow (true|false) show arrow default true
22220  * 
22221  * @constructor
22222  * Create a new TabGroup
22223  * @param {Object} config The config object
22224  */
22225
22226 Roo.bootstrap.TabGroup = function(config){
22227     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22228     if (!this.navId) {
22229         this.navId = Roo.id();
22230     }
22231     this.tabs = [];
22232     Roo.bootstrap.TabGroup.register(this);
22233     
22234 };
22235
22236 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22237     
22238     carousel : false,
22239     transition : false,
22240     bullets : 0,
22241     timer : 0,
22242     autoslide : false,
22243     slideFn : false,
22244     slideOnTouch : false,
22245     showarrow : true,
22246     
22247     getAutoCreate : function()
22248     {
22249         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22250         
22251         cfg.cls += ' tab-content';
22252         
22253         if (this.carousel) {
22254             cfg.cls += ' carousel slide';
22255             
22256             cfg.cn = [{
22257                cls : 'carousel-inner',
22258                cn : []
22259             }];
22260         
22261             if(this.bullets  && !Roo.isTouch){
22262                 
22263                 var bullets = {
22264                     cls : 'carousel-bullets',
22265                     cn : []
22266                 };
22267                
22268                 if(this.bullets_cls){
22269                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22270                 }
22271                 
22272                 bullets.cn.push({
22273                     cls : 'clear'
22274                 });
22275                 
22276                 cfg.cn[0].cn.push(bullets);
22277             }
22278             
22279             if(this.showarrow){
22280                 cfg.cn[0].cn.push({
22281                     tag : 'div',
22282                     class : 'carousel-arrow',
22283                     cn : [
22284                         {
22285                             tag : 'div',
22286                             class : 'carousel-prev',
22287                             cn : [
22288                                 {
22289                                     tag : 'i',
22290                                     class : 'fa fa-chevron-left'
22291                                 }
22292                             ]
22293                         },
22294                         {
22295                             tag : 'div',
22296                             class : 'carousel-next',
22297                             cn : [
22298                                 {
22299                                     tag : 'i',
22300                                     class : 'fa fa-chevron-right'
22301                                 }
22302                             ]
22303                         }
22304                     ]
22305                 });
22306             }
22307             
22308         }
22309         
22310         return cfg;
22311     },
22312     
22313     initEvents:  function()
22314     {
22315 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22316 //            this.el.on("touchstart", this.onTouchStart, this);
22317 //        }
22318         
22319         if(this.autoslide){
22320             var _this = this;
22321             
22322             this.slideFn = window.setInterval(function() {
22323                 _this.showPanelNext();
22324             }, this.timer);
22325         }
22326         
22327         if(this.showarrow){
22328             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22329             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22330         }
22331         
22332         
22333     },
22334     
22335 //    onTouchStart : function(e, el, o)
22336 //    {
22337 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22338 //            return;
22339 //        }
22340 //        
22341 //        this.showPanelNext();
22342 //    },
22343     
22344     
22345     getChildContainer : function()
22346     {
22347         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22348     },
22349     
22350     /**
22351     * register a Navigation item
22352     * @param {Roo.bootstrap.nav.Item} the navitem to add
22353     */
22354     register : function(item)
22355     {
22356         this.tabs.push( item);
22357         item.navId = this.navId; // not really needed..
22358         this.addBullet();
22359     
22360     },
22361     
22362     getActivePanel : function()
22363     {
22364         var r = false;
22365         Roo.each(this.tabs, function(t) {
22366             if (t.active) {
22367                 r = t;
22368                 return false;
22369             }
22370             return null;
22371         });
22372         return r;
22373         
22374     },
22375     getPanelByName : function(n)
22376     {
22377         var r = false;
22378         Roo.each(this.tabs, function(t) {
22379             if (t.tabId == n) {
22380                 r = t;
22381                 return false;
22382             }
22383             return null;
22384         });
22385         return r;
22386     },
22387     indexOfPanel : function(p)
22388     {
22389         var r = false;
22390         Roo.each(this.tabs, function(t,i) {
22391             if (t.tabId == p.tabId) {
22392                 r = i;
22393                 return false;
22394             }
22395             return null;
22396         });
22397         return r;
22398     },
22399     /**
22400      * show a specific panel
22401      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22402      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22403      */
22404     showPanel : function (pan)
22405     {
22406         if(this.transition || typeof(pan) == 'undefined'){
22407             Roo.log("waiting for the transitionend");
22408             return false;
22409         }
22410         
22411         if (typeof(pan) == 'number') {
22412             pan = this.tabs[pan];
22413         }
22414         
22415         if (typeof(pan) == 'string') {
22416             pan = this.getPanelByName(pan);
22417         }
22418         
22419         var cur = this.getActivePanel();
22420         
22421         if(!pan || !cur){
22422             Roo.log('pan or acitve pan is undefined');
22423             return false;
22424         }
22425         
22426         if (pan.tabId == this.getActivePanel().tabId) {
22427             return true;
22428         }
22429         
22430         if (false === cur.fireEvent('beforedeactivate')) {
22431             return false;
22432         }
22433         
22434         if(this.bullets > 0 && !Roo.isTouch){
22435             this.setActiveBullet(this.indexOfPanel(pan));
22436         }
22437         
22438         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22439             
22440             //class="carousel-item carousel-item-next carousel-item-left"
22441             
22442             this.transition = true;
22443             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22444             var lr = dir == 'next' ? 'left' : 'right';
22445             pan.el.addClass(dir); // or prev
22446             pan.el.addClass('carousel-item-' + dir); // or prev
22447             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22448             cur.el.addClass(lr); // or right
22449             pan.el.addClass(lr);
22450             cur.el.addClass('carousel-item-' +lr); // or right
22451             pan.el.addClass('carousel-item-' +lr);
22452             
22453             
22454             var _this = this;
22455             cur.el.on('transitionend', function() {
22456                 Roo.log("trans end?");
22457                 
22458                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22459                 pan.setActive(true);
22460                 
22461                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22462                 cur.setActive(false);
22463                 
22464                 _this.transition = false;
22465                 
22466             }, this, { single:  true } );
22467             
22468             return true;
22469         }
22470         
22471         cur.setActive(false);
22472         pan.setActive(true);
22473         
22474         return true;
22475         
22476     },
22477     showPanelNext : function()
22478     {
22479         var i = this.indexOfPanel(this.getActivePanel());
22480         
22481         if (i >= this.tabs.length - 1 && !this.autoslide) {
22482             return;
22483         }
22484         
22485         if (i >= this.tabs.length - 1 && this.autoslide) {
22486             i = -1;
22487         }
22488         
22489         this.showPanel(this.tabs[i+1]);
22490     },
22491     
22492     showPanelPrev : function()
22493     {
22494         var i = this.indexOfPanel(this.getActivePanel());
22495         
22496         if (i  < 1 && !this.autoslide) {
22497             return;
22498         }
22499         
22500         if (i < 1 && this.autoslide) {
22501             i = this.tabs.length;
22502         }
22503         
22504         this.showPanel(this.tabs[i-1]);
22505     },
22506     
22507     
22508     addBullet: function()
22509     {
22510         if(!this.bullets || Roo.isTouch){
22511             return;
22512         }
22513         var ctr = this.el.select('.carousel-bullets',true).first();
22514         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22515         var bullet = ctr.createChild({
22516             cls : 'bullet bullet-' + i
22517         },ctr.dom.lastChild);
22518         
22519         
22520         var _this = this;
22521         
22522         bullet.on('click', (function(e, el, o, ii, t){
22523
22524             e.preventDefault();
22525
22526             this.showPanel(ii);
22527
22528             if(this.autoslide && this.slideFn){
22529                 clearInterval(this.slideFn);
22530                 this.slideFn = window.setInterval(function() {
22531                     _this.showPanelNext();
22532                 }, this.timer);
22533             }
22534
22535         }).createDelegate(this, [i, bullet], true));
22536                 
22537         
22538     },
22539      
22540     setActiveBullet : function(i)
22541     {
22542         if(Roo.isTouch){
22543             return;
22544         }
22545         
22546         Roo.each(this.el.select('.bullet', true).elements, function(el){
22547             el.removeClass('selected');
22548         });
22549
22550         var bullet = this.el.select('.bullet-' + i, true).first();
22551         
22552         if(!bullet){
22553             return;
22554         }
22555         
22556         bullet.addClass('selected');
22557     }
22558     
22559     
22560   
22561 });
22562
22563  
22564
22565  
22566  
22567 Roo.apply(Roo.bootstrap.TabGroup, {
22568     
22569     groups: {},
22570      /**
22571     * register a Navigation Group
22572     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22573     */
22574     register : function(navgrp)
22575     {
22576         this.groups[navgrp.navId] = navgrp;
22577         
22578     },
22579     /**
22580     * fetch a Navigation Group based on the navigation ID
22581     * if one does not exist , it will get created.
22582     * @param {string} the navgroup to add
22583     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22584     */
22585     get: function(navId) {
22586         if (typeof(this.groups[navId]) == 'undefined') {
22587             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22588         }
22589         return this.groups[navId] ;
22590     }
22591     
22592     
22593     
22594 });
22595
22596  /*
22597  * - LGPL
22598  *
22599  * TabPanel
22600  * 
22601  */
22602
22603 /**
22604  * @class Roo.bootstrap.TabPanel
22605  * @extends Roo.bootstrap.Component
22606  * @children Roo.bootstrap.Component
22607  * Bootstrap TabPanel class
22608  * @cfg {Boolean} active panel active
22609  * @cfg {String} html panel content
22610  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22611  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22612  * @cfg {String} href click to link..
22613  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22614  * 
22615  * 
22616  * @constructor
22617  * Create a new TabPanel
22618  * @param {Object} config The config object
22619  */
22620
22621 Roo.bootstrap.TabPanel = function(config){
22622     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22623     this.addEvents({
22624         /**
22625              * @event changed
22626              * Fires when the active status changes
22627              * @param {Roo.bootstrap.TabPanel} this
22628              * @param {Boolean} state the new state
22629             
22630          */
22631         'changed': true,
22632         /**
22633              * @event beforedeactivate
22634              * Fires before a tab is de-activated - can be used to do validation on a form.
22635              * @param {Roo.bootstrap.TabPanel} this
22636              * @return {Boolean} false if there is an error
22637             
22638          */
22639         'beforedeactivate': true
22640      });
22641     
22642     this.tabId = this.tabId || Roo.id();
22643   
22644 };
22645
22646 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22647     
22648     active: false,
22649     html: false,
22650     tabId: false,
22651     navId : false,
22652     href : '',
22653     touchSlide : false,
22654     getAutoCreate : function(){
22655         
22656         
22657         var cfg = {
22658             tag: 'div',
22659             // item is needed for carousel - not sure if it has any effect otherwise
22660             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22661             html: this.html || ''
22662         };
22663         
22664         if(this.active){
22665             cfg.cls += ' active';
22666         }
22667         
22668         if(this.tabId){
22669             cfg.tabId = this.tabId;
22670         }
22671         
22672         
22673         
22674         return cfg;
22675     },
22676     
22677     initEvents:  function()
22678     {
22679         var p = this.parent();
22680         
22681         this.navId = this.navId || p.navId;
22682         
22683         if (typeof(this.navId) != 'undefined') {
22684             // not really needed.. but just in case.. parent should be a NavGroup.
22685             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22686             
22687             tg.register(this);
22688             
22689             var i = tg.tabs.length - 1;
22690             
22691             if(this.active && tg.bullets > 0 && i < tg.bullets){
22692                 tg.setActiveBullet(i);
22693             }
22694         }
22695         
22696         this.el.on('click', this.onClick, this);
22697         
22698         if(Roo.isTouch && this.touchSlide){
22699             this.el.on("touchstart", this.onTouchStart, this);
22700             this.el.on("touchmove", this.onTouchMove, this);
22701             this.el.on("touchend", this.onTouchEnd, this);
22702         }
22703         
22704     },
22705     
22706     onRender : function(ct, position)
22707     {
22708         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22709     },
22710     
22711     setActive : function(state)
22712     {
22713         Roo.log("panel - set active " + this.tabId + "=" + state);
22714         
22715         this.active = state;
22716         if (!state) {
22717             this.el.removeClass('active');
22718             
22719         } else  if (!this.el.hasClass('active')) {
22720             this.el.addClass('active');
22721         }
22722         
22723         this.fireEvent('changed', this, state);
22724     },
22725     
22726     onClick : function(e)
22727     {
22728         e.preventDefault();
22729         
22730         if(!this.href.length){
22731             return;
22732         }
22733         
22734         window.location.href = this.href;
22735     },
22736     
22737     startX : 0,
22738     startY : 0,
22739     endX : 0,
22740     endY : 0,
22741     swiping : false,
22742     
22743     onTouchStart : function(e)
22744     {
22745         this.swiping = false;
22746         
22747         this.startX = e.browserEvent.touches[0].clientX;
22748         this.startY = e.browserEvent.touches[0].clientY;
22749     },
22750     
22751     onTouchMove : function(e)
22752     {
22753         this.swiping = true;
22754         
22755         this.endX = e.browserEvent.touches[0].clientX;
22756         this.endY = e.browserEvent.touches[0].clientY;
22757     },
22758     
22759     onTouchEnd : function(e)
22760     {
22761         if(!this.swiping){
22762             this.onClick(e);
22763             return;
22764         }
22765         
22766         var tabGroup = this.parent();
22767         
22768         if(this.endX > this.startX){ // swiping right
22769             tabGroup.showPanelPrev();
22770             return;
22771         }
22772         
22773         if(this.startX > this.endX){ // swiping left
22774             tabGroup.showPanelNext();
22775             return;
22776         }
22777     }
22778     
22779     
22780 });
22781  
22782
22783  
22784
22785  /*
22786  * - LGPL
22787  *
22788  * DateField
22789  * 
22790  */
22791
22792 /**
22793  * @class Roo.bootstrap.form.DateField
22794  * @extends Roo.bootstrap.form.Input
22795  * Bootstrap DateField class
22796  * @cfg {Number} weekStart default 0
22797  * @cfg {String} viewMode default empty, (months|years)
22798  * @cfg {String} minViewMode default empty, (months|years)
22799  * @cfg {Number} startDate default -Infinity
22800  * @cfg {Number} endDate default Infinity
22801  * @cfg {Boolean} todayHighlight default false
22802  * @cfg {Boolean} todayBtn default false
22803  * @cfg {Boolean} calendarWeeks default false
22804  * @cfg {Object} daysOfWeekDisabled default empty
22805  * @cfg {Boolean} singleMode default false (true | false)
22806  * 
22807  * @cfg {Boolean} keyboardNavigation default true
22808  * @cfg {String} language default en
22809  * 
22810  * @constructor
22811  * Create a new DateField
22812  * @param {Object} config The config object
22813  */
22814
22815 Roo.bootstrap.form.DateField = function(config){
22816     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22817      this.addEvents({
22818             /**
22819              * @event show
22820              * Fires when this field show.
22821              * @param {Roo.bootstrap.form.DateField} this
22822              * @param {Mixed} date The date value
22823              */
22824             show : true,
22825             /**
22826              * @event show
22827              * Fires when this field hide.
22828              * @param {Roo.bootstrap.form.DateField} this
22829              * @param {Mixed} date The date value
22830              */
22831             hide : true,
22832             /**
22833              * @event select
22834              * Fires when select a date.
22835              * @param {Roo.bootstrap.form.DateField} this
22836              * @param {Mixed} date The date value
22837              */
22838             select : true,
22839             /**
22840              * @event beforeselect
22841              * Fires when before select a date.
22842              * @param {Roo.bootstrap.form.DateField} this
22843              * @param {Mixed} date The date value
22844              */
22845             beforeselect : true
22846         });
22847 };
22848
22849 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22850     
22851     /**
22852      * @cfg {String} format
22853      * The default date format string which can be overriden for localization support.  The format must be
22854      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22855      */
22856     format : "m/d/y",
22857     /**
22858      * @cfg {String} altFormats
22859      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22860      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22861      */
22862     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22863     
22864     weekStart : 0,
22865     
22866     viewMode : '',
22867     
22868     minViewMode : '',
22869     
22870     todayHighlight : false,
22871     
22872     todayBtn: false,
22873     
22874     language: 'en',
22875     
22876     keyboardNavigation: true,
22877     
22878     calendarWeeks: false,
22879     
22880     startDate: -Infinity,
22881     
22882     endDate: Infinity,
22883     
22884     daysOfWeekDisabled: [],
22885     
22886     _events: [],
22887     
22888     singleMode : false,
22889     
22890     UTCDate: function()
22891     {
22892         return new Date(Date.UTC.apply(Date, arguments));
22893     },
22894     
22895     UTCToday: function()
22896     {
22897         var today = new Date();
22898         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22899     },
22900     
22901     getDate: function() {
22902             var d = this.getUTCDate();
22903             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22904     },
22905     
22906     getUTCDate: function() {
22907             return this.date;
22908     },
22909     
22910     setDate: function(d) {
22911             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22912     },
22913     
22914     setUTCDate: function(d) {
22915             this.date = d;
22916             this.setValue(this.formatDate(this.date));
22917     },
22918         
22919     onRender: function(ct, position)
22920     {
22921         
22922         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22923         
22924         this.language = this.language || 'en';
22925         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22926         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22927         
22928         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22929         this.format = this.format || 'm/d/y';
22930         this.isInline = false;
22931         this.isInput = true;
22932         this.component = this.el.select('.add-on', true).first() || false;
22933         this.component = (this.component && this.component.length === 0) ? false : this.component;
22934         this.hasInput = this.component && this.inputEl().length;
22935         
22936         if (typeof(this.minViewMode === 'string')) {
22937             switch (this.minViewMode) {
22938                 case 'months':
22939                     this.minViewMode = 1;
22940                     break;
22941                 case 'years':
22942                     this.minViewMode = 2;
22943                     break;
22944                 default:
22945                     this.minViewMode = 0;
22946                     break;
22947             }
22948         }
22949         
22950         if (typeof(this.viewMode === 'string')) {
22951             switch (this.viewMode) {
22952                 case 'months':
22953                     this.viewMode = 1;
22954                     break;
22955                 case 'years':
22956                     this.viewMode = 2;
22957                     break;
22958                 default:
22959                     this.viewMode = 0;
22960                     break;
22961             }
22962         }
22963                 
22964         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22965         
22966 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22967         
22968         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22969         
22970         this.picker().on('mousedown', this.onMousedown, this);
22971         this.picker().on('click', this.onClick, this);
22972         
22973         this.picker().addClass('datepicker-dropdown');
22974         
22975         this.startViewMode = this.viewMode;
22976         
22977         if(this.singleMode){
22978             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22979                 v.setVisibilityMode(Roo.Element.DISPLAY);
22980                 v.hide();
22981             });
22982             
22983             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22984                 v.setStyle('width', '189px');
22985             });
22986         }
22987         
22988         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22989             if(!this.calendarWeeks){
22990                 v.remove();
22991                 return;
22992             }
22993             
22994             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22995             v.attr('colspan', function(i, val){
22996                 return parseInt(val) + 1;
22997             });
22998         });
22999                         
23000         
23001         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23002         
23003         this.setStartDate(this.startDate);
23004         this.setEndDate(this.endDate);
23005         
23006         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23007         
23008         this.fillDow();
23009         this.fillMonths();
23010         this.update();
23011         this.showMode();
23012         
23013         if(this.isInline) {
23014             this.showPopup();
23015         }
23016     },
23017     
23018     picker : function()
23019     {
23020         return this.pickerEl;
23021 //        return this.el.select('.datepicker', true).first();
23022     },
23023     
23024     fillDow: function()
23025     {
23026         var dowCnt = this.weekStart;
23027         
23028         var dow = {
23029             tag: 'tr',
23030             cn: [
23031                 
23032             ]
23033         };
23034         
23035         if(this.calendarWeeks){
23036             dow.cn.push({
23037                 tag: 'th',
23038                 cls: 'cw',
23039                 html: '&nbsp;'
23040             })
23041         }
23042         
23043         while (dowCnt < this.weekStart + 7) {
23044             dow.cn.push({
23045                 tag: 'th',
23046                 cls: 'dow',
23047                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23048             });
23049         }
23050         
23051         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23052     },
23053     
23054     fillMonths: function()
23055     {    
23056         var i = 0;
23057         var months = this.picker().select('>.datepicker-months td', true).first();
23058         
23059         months.dom.innerHTML = '';
23060         
23061         while (i < 12) {
23062             var month = {
23063                 tag: 'span',
23064                 cls: 'month',
23065                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23066             };
23067             
23068             months.createChild(month);
23069         }
23070         
23071     },
23072     
23073     update: function()
23074     {
23075         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;
23076         
23077         if (this.date < this.startDate) {
23078             this.viewDate = new Date(this.startDate);
23079         } else if (this.date > this.endDate) {
23080             this.viewDate = new Date(this.endDate);
23081         } else {
23082             this.viewDate = new Date(this.date);
23083         }
23084         
23085         this.fill();
23086     },
23087     
23088     fill: function() 
23089     {
23090         var d = new Date(this.viewDate),
23091                 year = d.getUTCFullYear(),
23092                 month = d.getUTCMonth(),
23093                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23094                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23095                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23096                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23097                 currentDate = this.date && this.date.valueOf(),
23098                 today = this.UTCToday();
23099         
23100         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23101         
23102 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23103         
23104 //        this.picker.select('>tfoot th.today').
23105 //                                              .text(dates[this.language].today)
23106 //                                              .toggle(this.todayBtn !== false);
23107     
23108         this.updateNavArrows();
23109         this.fillMonths();
23110                                                 
23111         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23112         
23113         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23114          
23115         prevMonth.setUTCDate(day);
23116         
23117         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23118         
23119         var nextMonth = new Date(prevMonth);
23120         
23121         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23122         
23123         nextMonth = nextMonth.valueOf();
23124         
23125         var fillMonths = false;
23126         
23127         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23128         
23129         while(prevMonth.valueOf() <= nextMonth) {
23130             var clsName = '';
23131             
23132             if (prevMonth.getUTCDay() === this.weekStart) {
23133                 if(fillMonths){
23134                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23135                 }
23136                     
23137                 fillMonths = {
23138                     tag: 'tr',
23139                     cn: []
23140                 };
23141                 
23142                 if(this.calendarWeeks){
23143                     // ISO 8601: First week contains first thursday.
23144                     // ISO also states week starts on Monday, but we can be more abstract here.
23145                     var
23146                     // Start of current week: based on weekstart/current date
23147                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23148                     // Thursday of this week
23149                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23150                     // First Thursday of year, year from thursday
23151                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23152                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23153                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23154                     
23155                     fillMonths.cn.push({
23156                         tag: 'td',
23157                         cls: 'cw',
23158                         html: calWeek
23159                     });
23160                 }
23161             }
23162             
23163             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23164                 clsName += ' old';
23165             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23166                 clsName += ' new';
23167             }
23168             if (this.todayHighlight &&
23169                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23170                 prevMonth.getUTCMonth() == today.getMonth() &&
23171                 prevMonth.getUTCDate() == today.getDate()) {
23172                 clsName += ' today';
23173             }
23174             
23175             if (currentDate && prevMonth.valueOf() === currentDate) {
23176                 clsName += ' active';
23177             }
23178             
23179             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23180                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23181                     clsName += ' disabled';
23182             }
23183             
23184             fillMonths.cn.push({
23185                 tag: 'td',
23186                 cls: 'day ' + clsName,
23187                 html: prevMonth.getDate()
23188             });
23189             
23190             prevMonth.setDate(prevMonth.getDate()+1);
23191         }
23192           
23193         var currentYear = this.date && this.date.getUTCFullYear();
23194         var currentMonth = this.date && this.date.getUTCMonth();
23195         
23196         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23197         
23198         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23199             v.removeClass('active');
23200             
23201             if(currentYear === year && k === currentMonth){
23202                 v.addClass('active');
23203             }
23204             
23205             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23206                 v.addClass('disabled');
23207             }
23208             
23209         });
23210         
23211         
23212         year = parseInt(year/10, 10) * 10;
23213         
23214         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23215         
23216         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23217         
23218         year -= 1;
23219         for (var i = -1; i < 11; i++) {
23220             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23221                 tag: 'span',
23222                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23223                 html: year
23224             });
23225             
23226             year += 1;
23227         }
23228     },
23229     
23230     showMode: function(dir) 
23231     {
23232         if (dir) {
23233             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23234         }
23235         
23236         Roo.each(this.picker().select('>div',true).elements, function(v){
23237             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23238             v.hide();
23239         });
23240         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23241     },
23242     
23243     place: function()
23244     {
23245         if(this.isInline) {
23246             return;
23247         }
23248         
23249         this.picker().removeClass(['bottom', 'top']);
23250         
23251         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23252             /*
23253              * place to the top of element!
23254              *
23255              */
23256             
23257             this.picker().addClass('top');
23258             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23259             
23260             return;
23261         }
23262         
23263         this.picker().addClass('bottom');
23264         
23265         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23266     },
23267     
23268     parseDate : function(value)
23269     {
23270         if(!value || value instanceof Date){
23271             return value;
23272         }
23273         var v = Date.parseDate(value, this.format);
23274         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23275             v = Date.parseDate(value, 'Y-m-d');
23276         }
23277         if(!v && this.altFormats){
23278             if(!this.altFormatsArray){
23279                 this.altFormatsArray = this.altFormats.split("|");
23280             }
23281             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23282                 v = Date.parseDate(value, this.altFormatsArray[i]);
23283             }
23284         }
23285         return v;
23286     },
23287     
23288     formatDate : function(date, fmt)
23289     {   
23290         return (!date || !(date instanceof Date)) ?
23291         date : date.dateFormat(fmt || this.format);
23292     },
23293     
23294     onFocus : function()
23295     {
23296         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23297         this.showPopup();
23298     },
23299     
23300     onBlur : function()
23301     {
23302         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23303         
23304         var d = this.inputEl().getValue();
23305         
23306         this.setValue(d);
23307                 
23308         this.hidePopup();
23309     },
23310     
23311     showPopup : function()
23312     {
23313         this.picker().show();
23314         this.update();
23315         this.place();
23316         
23317         this.fireEvent('showpopup', this, this.date);
23318     },
23319     
23320     hidePopup : function()
23321     {
23322         if(this.isInline) {
23323             return;
23324         }
23325         this.picker().hide();
23326         this.viewMode = this.startViewMode;
23327         this.showMode();
23328         
23329         this.fireEvent('hidepopup', this, this.date);
23330         
23331     },
23332     
23333     onMousedown: function(e)
23334     {
23335         e.stopPropagation();
23336         e.preventDefault();
23337     },
23338     
23339     keyup: function(e)
23340     {
23341         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23342         this.update();
23343     },
23344
23345     setValue: function(v)
23346     {
23347         if(this.fireEvent('beforeselect', this, v) !== false){
23348             var d = new Date(this.parseDate(v) ).clearTime();
23349         
23350             if(isNaN(d.getTime())){
23351                 this.date = this.viewDate = '';
23352                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23353                 return;
23354             }
23355
23356             v = this.formatDate(d);
23357
23358             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23359
23360             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23361
23362             this.update();
23363
23364             this.fireEvent('select', this, this.date);
23365         }
23366     },
23367     
23368     getValue: function()
23369     {
23370         return this.formatDate(this.date);
23371     },
23372     
23373     fireKey: function(e)
23374     {
23375         if (!this.picker().isVisible()){
23376             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23377                 this.showPopup();
23378             }
23379             return;
23380         }
23381         
23382         var dateChanged = false,
23383         dir, day, month,
23384         newDate, newViewDate;
23385         
23386         switch(e.keyCode){
23387             case 27: // escape
23388                 this.hidePopup();
23389                 e.preventDefault();
23390                 break;
23391             case 37: // left
23392             case 39: // right
23393                 if (!this.keyboardNavigation) {
23394                     break;
23395                 }
23396                 dir = e.keyCode == 37 ? -1 : 1;
23397                 
23398                 if (e.ctrlKey){
23399                     newDate = this.moveYear(this.date, dir);
23400                     newViewDate = this.moveYear(this.viewDate, dir);
23401                 } else if (e.shiftKey){
23402                     newDate = this.moveMonth(this.date, dir);
23403                     newViewDate = this.moveMonth(this.viewDate, dir);
23404                 } else {
23405                     newDate = new Date(this.date);
23406                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23407                     newViewDate = new Date(this.viewDate);
23408                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23409                 }
23410                 if (this.dateWithinRange(newDate)){
23411                     this.date = newDate;
23412                     this.viewDate = newViewDate;
23413                     this.setValue(this.formatDate(this.date));
23414 //                    this.update();
23415                     e.preventDefault();
23416                     dateChanged = true;
23417                 }
23418                 break;
23419             case 38: // up
23420             case 40: // down
23421                 if (!this.keyboardNavigation) {
23422                     break;
23423                 }
23424                 dir = e.keyCode == 38 ? -1 : 1;
23425                 if (e.ctrlKey){
23426                     newDate = this.moveYear(this.date, dir);
23427                     newViewDate = this.moveYear(this.viewDate, dir);
23428                 } else if (e.shiftKey){
23429                     newDate = this.moveMonth(this.date, dir);
23430                     newViewDate = this.moveMonth(this.viewDate, dir);
23431                 } else {
23432                     newDate = new Date(this.date);
23433                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23434                     newViewDate = new Date(this.viewDate);
23435                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23436                 }
23437                 if (this.dateWithinRange(newDate)){
23438                     this.date = newDate;
23439                     this.viewDate = newViewDate;
23440                     this.setValue(this.formatDate(this.date));
23441 //                    this.update();
23442                     e.preventDefault();
23443                     dateChanged = true;
23444                 }
23445                 break;
23446             case 13: // enter
23447                 this.setValue(this.formatDate(this.date));
23448                 this.hidePopup();
23449                 e.preventDefault();
23450                 break;
23451             case 9: // tab
23452                 this.setValue(this.formatDate(this.date));
23453                 this.hidePopup();
23454                 break;
23455             case 16: // shift
23456             case 17: // ctrl
23457             case 18: // alt
23458                 break;
23459             default :
23460                 this.hidePopup();
23461                 
23462         }
23463     },
23464     
23465     
23466     onClick: function(e) 
23467     {
23468         e.stopPropagation();
23469         e.preventDefault();
23470         
23471         var target = e.getTarget();
23472         
23473         if(target.nodeName.toLowerCase() === 'i'){
23474             target = Roo.get(target).dom.parentNode;
23475         }
23476         
23477         var nodeName = target.nodeName;
23478         var className = target.className;
23479         var html = target.innerHTML;
23480         //Roo.log(nodeName);
23481         
23482         switch(nodeName.toLowerCase()) {
23483             case 'th':
23484                 switch(className) {
23485                     case 'switch':
23486                         this.showMode(1);
23487                         break;
23488                     case 'prev':
23489                     case 'next':
23490                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23491                         switch(this.viewMode){
23492                                 case 0:
23493                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23494                                         break;
23495                                 case 1:
23496                                 case 2:
23497                                         this.viewDate = this.moveYear(this.viewDate, dir);
23498                                         break;
23499                         }
23500                         this.fill();
23501                         break;
23502                     case 'today':
23503                         var date = new Date();
23504                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23505 //                        this.fill()
23506                         this.setValue(this.formatDate(this.date));
23507                         
23508                         this.hidePopup();
23509                         break;
23510                 }
23511                 break;
23512             case 'span':
23513                 if (className.indexOf('disabled') < 0) {
23514                 if (!this.viewDate) {
23515                     this.viewDate = new Date();
23516                 }
23517                 this.viewDate.setUTCDate(1);
23518                     if (className.indexOf('month') > -1) {
23519                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23520                     } else {
23521                         var year = parseInt(html, 10) || 0;
23522                         this.viewDate.setUTCFullYear(year);
23523                         
23524                     }
23525                     
23526                     if(this.singleMode){
23527                         this.setValue(this.formatDate(this.viewDate));
23528                         this.hidePopup();
23529                         return;
23530                     }
23531                     
23532                     this.showMode(-1);
23533                     this.fill();
23534                 }
23535                 break;
23536                 
23537             case 'td':
23538                 //Roo.log(className);
23539                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23540                     var day = parseInt(html, 10) || 1;
23541                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23542                         month = (this.viewDate || new Date()).getUTCMonth();
23543
23544                     if (className.indexOf('old') > -1) {
23545                         if(month === 0 ){
23546                             month = 11;
23547                             year -= 1;
23548                         }else{
23549                             month -= 1;
23550                         }
23551                     } else if (className.indexOf('new') > -1) {
23552                         if (month == 11) {
23553                             month = 0;
23554                             year += 1;
23555                         } else {
23556                             month += 1;
23557                         }
23558                     }
23559                     //Roo.log([year,month,day]);
23560                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23561                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23562 //                    this.fill();
23563                     //Roo.log(this.formatDate(this.date));
23564                     this.setValue(this.formatDate(this.date));
23565                     this.hidePopup();
23566                 }
23567                 break;
23568         }
23569     },
23570     
23571     setStartDate: function(startDate)
23572     {
23573         this.startDate = startDate || -Infinity;
23574         if (this.startDate !== -Infinity) {
23575             this.startDate = this.parseDate(this.startDate);
23576         }
23577         this.update();
23578         this.updateNavArrows();
23579     },
23580
23581     setEndDate: function(endDate)
23582     {
23583         this.endDate = endDate || Infinity;
23584         if (this.endDate !== Infinity) {
23585             this.endDate = this.parseDate(this.endDate);
23586         }
23587         this.update();
23588         this.updateNavArrows();
23589     },
23590     
23591     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23592     {
23593         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23594         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23595             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23596         }
23597         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23598             return parseInt(d, 10);
23599         });
23600         this.update();
23601         this.updateNavArrows();
23602     },
23603     
23604     updateNavArrows: function() 
23605     {
23606         if(this.singleMode){
23607             return;
23608         }
23609         
23610         var d = new Date(this.viewDate),
23611         year = d.getUTCFullYear(),
23612         month = d.getUTCMonth();
23613         
23614         Roo.each(this.picker().select('.prev', true).elements, function(v){
23615             v.show();
23616             switch (this.viewMode) {
23617                 case 0:
23618
23619                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23620                         v.hide();
23621                     }
23622                     break;
23623                 case 1:
23624                 case 2:
23625                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23626                         v.hide();
23627                     }
23628                     break;
23629             }
23630         });
23631         
23632         Roo.each(this.picker().select('.next', true).elements, function(v){
23633             v.show();
23634             switch (this.viewMode) {
23635                 case 0:
23636
23637                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23638                         v.hide();
23639                     }
23640                     break;
23641                 case 1:
23642                 case 2:
23643                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23644                         v.hide();
23645                     }
23646                     break;
23647             }
23648         })
23649     },
23650     
23651     moveMonth: function(date, dir)
23652     {
23653         if (!dir) {
23654             return date;
23655         }
23656         var new_date = new Date(date.valueOf()),
23657         day = new_date.getUTCDate(),
23658         month = new_date.getUTCMonth(),
23659         mag = Math.abs(dir),
23660         new_month, test;
23661         dir = dir > 0 ? 1 : -1;
23662         if (mag == 1){
23663             test = dir == -1
23664             // If going back one month, make sure month is not current month
23665             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23666             ? function(){
23667                 return new_date.getUTCMonth() == month;
23668             }
23669             // If going forward one month, make sure month is as expected
23670             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23671             : function(){
23672                 return new_date.getUTCMonth() != new_month;
23673             };
23674             new_month = month + dir;
23675             new_date.setUTCMonth(new_month);
23676             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23677             if (new_month < 0 || new_month > 11) {
23678                 new_month = (new_month + 12) % 12;
23679             }
23680         } else {
23681             // For magnitudes >1, move one month at a time...
23682             for (var i=0; i<mag; i++) {
23683                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23684                 new_date = this.moveMonth(new_date, dir);
23685             }
23686             // ...then reset the day, keeping it in the new month
23687             new_month = new_date.getUTCMonth();
23688             new_date.setUTCDate(day);
23689             test = function(){
23690                 return new_month != new_date.getUTCMonth();
23691             };
23692         }
23693         // Common date-resetting loop -- if date is beyond end of month, make it
23694         // end of month
23695         while (test()){
23696             new_date.setUTCDate(--day);
23697             new_date.setUTCMonth(new_month);
23698         }
23699         return new_date;
23700     },
23701
23702     moveYear: function(date, dir)
23703     {
23704         return this.moveMonth(date, dir*12);
23705     },
23706
23707     dateWithinRange: function(date)
23708     {
23709         return date >= this.startDate && date <= this.endDate;
23710     },
23711
23712     
23713     remove: function() 
23714     {
23715         this.picker().remove();
23716     },
23717     
23718     validateValue : function(value)
23719     {
23720         if(this.getVisibilityEl().hasClass('hidden')){
23721             return true;
23722         }
23723         
23724         if(value.length < 1)  {
23725             if(this.allowBlank){
23726                 return true;
23727             }
23728             return false;
23729         }
23730         
23731         if(value.length < this.minLength){
23732             return false;
23733         }
23734         if(value.length > this.maxLength){
23735             return false;
23736         }
23737         if(this.vtype){
23738             var vt = Roo.form.VTypes;
23739             if(!vt[this.vtype](value, this)){
23740                 return false;
23741             }
23742         }
23743         if(typeof this.validator == "function"){
23744             var msg = this.validator(value);
23745             if(msg !== true){
23746                 return false;
23747             }
23748         }
23749         
23750         if(this.regex && !this.regex.test(value)){
23751             return false;
23752         }
23753         
23754         if(typeof(this.parseDate(value)) == 'undefined'){
23755             return false;
23756         }
23757         
23758         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23759             return false;
23760         }      
23761         
23762         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23763             return false;
23764         } 
23765         
23766         
23767         return true;
23768     },
23769     
23770     reset : function()
23771     {
23772         this.date = this.viewDate = '';
23773         
23774         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23775     }
23776    
23777 });
23778
23779 Roo.apply(Roo.bootstrap.form.DateField,  {
23780     
23781     head : {
23782         tag: 'thead',
23783         cn: [
23784         {
23785             tag: 'tr',
23786             cn: [
23787             {
23788                 tag: 'th',
23789                 cls: 'prev',
23790                 html: '<i class="fa fa-arrow-left"/>'
23791             },
23792             {
23793                 tag: 'th',
23794                 cls: 'switch',
23795                 colspan: '5'
23796             },
23797             {
23798                 tag: 'th',
23799                 cls: 'next',
23800                 html: '<i class="fa fa-arrow-right"/>'
23801             }
23802
23803             ]
23804         }
23805         ]
23806     },
23807     
23808     content : {
23809         tag: 'tbody',
23810         cn: [
23811         {
23812             tag: 'tr',
23813             cn: [
23814             {
23815                 tag: 'td',
23816                 colspan: '7'
23817             }
23818             ]
23819         }
23820         ]
23821     },
23822     
23823     footer : {
23824         tag: 'tfoot',
23825         cn: [
23826         {
23827             tag: 'tr',
23828             cn: [
23829             {
23830                 tag: 'th',
23831                 colspan: '7',
23832                 cls: 'today'
23833             }
23834                     
23835             ]
23836         }
23837         ]
23838     },
23839     
23840     dates:{
23841         en: {
23842             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23843             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23844             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23845             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23846             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23847             today: "Today"
23848         }
23849     },
23850     
23851     modes: [
23852     {
23853         clsName: 'days',
23854         navFnc: 'Month',
23855         navStep: 1
23856     },
23857     {
23858         clsName: 'months',
23859         navFnc: 'FullYear',
23860         navStep: 1
23861     },
23862     {
23863         clsName: 'years',
23864         navFnc: 'FullYear',
23865         navStep: 10
23866     }]
23867 });
23868
23869 Roo.apply(Roo.bootstrap.form.DateField,  {
23870   
23871     template : {
23872         tag: 'div',
23873         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23874         cn: [
23875         {
23876             tag: 'div',
23877             cls: 'datepicker-days',
23878             cn: [
23879             {
23880                 tag: 'table',
23881                 cls: 'table-condensed',
23882                 cn:[
23883                 Roo.bootstrap.form.DateField.head,
23884                 {
23885                     tag: 'tbody'
23886                 },
23887                 Roo.bootstrap.form.DateField.footer
23888                 ]
23889             }
23890             ]
23891         },
23892         {
23893             tag: 'div',
23894             cls: 'datepicker-months',
23895             cn: [
23896             {
23897                 tag: 'table',
23898                 cls: 'table-condensed',
23899                 cn:[
23900                 Roo.bootstrap.form.DateField.head,
23901                 Roo.bootstrap.form.DateField.content,
23902                 Roo.bootstrap.form.DateField.footer
23903                 ]
23904             }
23905             ]
23906         },
23907         {
23908             tag: 'div',
23909             cls: 'datepicker-years',
23910             cn: [
23911             {
23912                 tag: 'table',
23913                 cls: 'table-condensed',
23914                 cn:[
23915                 Roo.bootstrap.form.DateField.head,
23916                 Roo.bootstrap.form.DateField.content,
23917                 Roo.bootstrap.form.DateField.footer
23918                 ]
23919             }
23920             ]
23921         }
23922         ]
23923     }
23924 });
23925
23926  
23927
23928  /*
23929  * - LGPL
23930  *
23931  * TimeField
23932  * 
23933  */
23934
23935 /**
23936  * @class Roo.bootstrap.form.TimeField
23937  * @extends Roo.bootstrap.form.Input
23938  * Bootstrap DateField class
23939  * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
23940  * 
23941  * 
23942  * @constructor
23943  * Create a new TimeField
23944  * @param {Object} config The config object
23945  */
23946
23947 Roo.bootstrap.form.TimeField = function(config){
23948     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23949     this.addEvents({
23950             /**
23951              * @event show
23952              * Fires when this field show.
23953              * @param {Roo.bootstrap.form.DateField} thisthis
23954              * @param {Mixed} date The date value
23955              */
23956             show : true,
23957             /**
23958              * @event show
23959              * Fires when this field hide.
23960              * @param {Roo.bootstrap.form.DateField} this
23961              * @param {Mixed} date The date value
23962              */
23963             hide : true,
23964             /**
23965              * @event select
23966              * Fires when select a date.
23967              * @param {Roo.bootstrap.form.DateField} this
23968              * @param {Mixed} date The date value
23969              */
23970             select : true
23971         });
23972 };
23973
23974 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23975     
23976     /**
23977      * @cfg {String} format
23978      * The default time format string which can be overriden for localization support.  The format must be
23979      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23980      */
23981     format : "H:i",
23982     minuteStep : 1,
23983
23984     getAutoCreate : function()
23985     {
23986         this.after = '<i class="fa far fa-clock"></i>';
23987         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23988         
23989          
23990     },
23991     onRender: function(ct, position)
23992     {
23993         
23994         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23995                 
23996         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23997         
23998         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23999         
24000         this.pop = this.picker().select('>.datepicker-time',true).first();
24001         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24002         
24003         this.picker().on('mousedown', this.onMousedown, this);
24004         this.picker().on('click', this.onClick, this);
24005         
24006         this.picker().addClass('datepicker-dropdown');
24007     
24008         this.fillTime();
24009         this.update();
24010             
24011         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24012         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24013         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24014         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24015         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24016         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24017
24018     },
24019     
24020     fireKey: function(e){
24021         if (!this.picker().isVisible()){
24022             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24023                 this.show();
24024             }
24025             return;
24026         }
24027
24028         e.preventDefault();
24029         
24030         switch(e.keyCode){
24031             case 27: // escape
24032                 this.hide();
24033                 break;
24034             case 37: // left
24035             case 39: // right
24036                 this.onTogglePeriod();
24037                 break;
24038             case 38: // up
24039                 this.onIncrementMinutes();
24040                 break;
24041             case 40: // down
24042                 this.onDecrementMinutes();
24043                 break;
24044             case 13: // enter
24045             case 9: // tab
24046                 this.setTime();
24047                 break;
24048         }
24049     },
24050     
24051     onClick: function(e) {
24052         e.stopPropagation();
24053         e.preventDefault();
24054     },
24055     
24056     picker : function()
24057     {
24058         return this.pickerEl;
24059     },
24060     
24061     fillTime: function()
24062     {    
24063         var time = this.pop.select('tbody', true).first();
24064         
24065         time.dom.innerHTML = '';
24066         
24067         time.createChild({
24068             tag: 'tr',
24069             cn: [
24070                 {
24071                     tag: 'td',
24072                     cn: [
24073                         {
24074                             tag: 'a',
24075                             href: '#',
24076                             cls: 'btn',
24077                             cn: [
24078                                 {
24079                                     tag: 'i',
24080                                     cls: 'hours-up fa fas fa-chevron-up'
24081                                 }
24082                             ]
24083                         } 
24084                     ]
24085                 },
24086                 {
24087                     tag: 'td',
24088                     cls: 'separator'
24089                 },
24090                 {
24091                     tag: 'td',
24092                     cn: [
24093                         {
24094                             tag: 'a',
24095                             href: '#',
24096                             cls: 'btn',
24097                             cn: [
24098                                 {
24099                                     tag: 'i',
24100                                     cls: 'minutes-up fa fas fa-chevron-up'
24101                                 }
24102                             ]
24103                         }
24104                     ]
24105                 },
24106                 {
24107                     tag: 'td',
24108                     cls: 'separator'
24109                 }
24110             ]
24111         });
24112         
24113         time.createChild({
24114             tag: 'tr',
24115             cn: [
24116                 {
24117                     tag: 'td',
24118                     cn: [
24119                         {
24120                             tag: 'span',
24121                             cls: 'timepicker-hour',
24122                             html: '00'
24123                         }  
24124                     ]
24125                 },
24126                 {
24127                     tag: 'td',
24128                     cls: 'separator',
24129                     html: ':'
24130                 },
24131                 {
24132                     tag: 'td',
24133                     cn: [
24134                         {
24135                             tag: 'span',
24136                             cls: 'timepicker-minute',
24137                             html: '00'
24138                         }  
24139                     ]
24140                 },
24141                 {
24142                     tag: 'td',
24143                     cls: 'separator'
24144                 },
24145                 {
24146                     tag: 'td',
24147                     cn: [
24148                         {
24149                             tag: 'button',
24150                             type: 'button',
24151                             cls: 'btn btn-primary period',
24152                             html: 'AM'
24153                             
24154                         }
24155                     ]
24156                 }
24157             ]
24158         });
24159         
24160         time.createChild({
24161             tag: 'tr',
24162             cn: [
24163                 {
24164                     tag: 'td',
24165                     cn: [
24166                         {
24167                             tag: 'a',
24168                             href: '#',
24169                             cls: 'btn',
24170                             cn: [
24171                                 {
24172                                     tag: 'span',
24173                                     cls: 'hours-down fa fas fa-chevron-down'
24174                                 }
24175                             ]
24176                         }
24177                     ]
24178                 },
24179                 {
24180                     tag: 'td',
24181                     cls: 'separator'
24182                 },
24183                 {
24184                     tag: 'td',
24185                     cn: [
24186                         {
24187                             tag: 'a',
24188                             href: '#',
24189                             cls: 'btn',
24190                             cn: [
24191                                 {
24192                                     tag: 'span',
24193                                     cls: 'minutes-down fa fas fa-chevron-down'
24194                                 }
24195                             ]
24196                         }
24197                     ]
24198                 },
24199                 {
24200                     tag: 'td',
24201                     cls: 'separator'
24202                 }
24203             ]
24204         });
24205         
24206     },
24207     
24208     update: function()
24209     {
24210         
24211         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24212         
24213         this.fill();
24214     },
24215     
24216     fill: function() 
24217     {
24218         var hours = this.time.getHours();
24219         var minutes = this.time.getMinutes();
24220         var period = 'AM';
24221         
24222         if(hours > 11){
24223             period = 'PM';
24224         }
24225         
24226         if(hours == 0){
24227             hours = 12;
24228         }
24229         
24230         
24231         if(hours > 12){
24232             hours = hours - 12;
24233         }
24234         
24235         if(hours < 10){
24236             hours = '0' + hours;
24237         }
24238         
24239         if(minutes < 10){
24240             minutes = '0' + minutes;
24241         }
24242         
24243         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24244         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24245         this.pop.select('button', true).first().dom.innerHTML = period;
24246         
24247     },
24248     
24249     place: function()
24250     {   
24251         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24252         
24253         var cls = ['bottom'];
24254         
24255         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24256             cls.pop();
24257             cls.push('top');
24258         }
24259         
24260         cls.push('right');
24261         
24262         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24263             cls.pop();
24264             cls.push('left');
24265         }
24266         //this.picker().setXY(20000,20000);
24267         this.picker().addClass(cls.join('-'));
24268         
24269         var _this = this;
24270         
24271         Roo.each(cls, function(c){
24272             if(c == 'bottom'){
24273                 (function() {
24274                  //  
24275                 }).defer(200);
24276                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24277                 //_this.picker().setTop(_this.inputEl().getHeight());
24278                 return;
24279             }
24280             if(c == 'top'){
24281                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24282                 
24283                 //_this.picker().setTop(0 - _this.picker().getHeight());
24284                 return;
24285             }
24286             /*
24287             if(c == 'left'){
24288                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24289                 return;
24290             }
24291             if(c == 'right'){
24292                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24293                 return;
24294             }
24295             */
24296         });
24297         
24298     },
24299   
24300     onFocus : function()
24301     {
24302         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24303         this.show();
24304     },
24305     
24306     onBlur : function()
24307     {
24308         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24309         this.hide();
24310     },
24311     
24312     show : function()
24313     {
24314         this.picker().show();
24315         this.pop.show();
24316         this.update();
24317         this.place();
24318         
24319         this.fireEvent('show', this, this.date);
24320     },
24321     
24322     hide : function()
24323     {
24324         this.picker().hide();
24325         this.pop.hide();
24326         
24327         this.fireEvent('hide', this, this.date);
24328     },
24329     
24330     setTime : function()
24331     {
24332         this.hide();
24333         this.setValue(this.time.format(this.format));
24334         
24335         this.fireEvent('select', this, this.date);
24336         
24337         
24338     },
24339     
24340     onMousedown: function(e){
24341         e.stopPropagation();
24342         e.preventDefault();
24343     },
24344     
24345     onIncrementHours: function()
24346     {
24347         Roo.log('onIncrementHours');
24348         this.time = this.time.add(Date.HOUR, 1);
24349         this.update();
24350         
24351     },
24352     
24353     onDecrementHours: function()
24354     {
24355         Roo.log('onDecrementHours');
24356         this.time = this.time.add(Date.HOUR, -1);
24357         this.update();
24358     },
24359     
24360     onIncrementMinutes: function()
24361     {
24362         Roo.log('onIncrementMinutes');
24363         var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24364         this.time = this.time.add(Date.MINUTE, minutesToAdd);
24365         this.update();
24366     },
24367     
24368     onDecrementMinutes: function()
24369     {
24370         Roo.log('onDecrementMinutes');
24371         var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24372         this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24373         this.update();
24374     },
24375     
24376     onTogglePeriod: function()
24377     {
24378         Roo.log('onTogglePeriod');
24379         this.time = this.time.add(Date.HOUR, 12);
24380         this.update();
24381     }
24382     
24383    
24384 });
24385  
24386
24387 Roo.apply(Roo.bootstrap.form.TimeField,  {
24388   
24389     template : {
24390         tag: 'div',
24391         cls: 'datepicker dropdown-menu',
24392         cn: [
24393             {
24394                 tag: 'div',
24395                 cls: 'datepicker-time',
24396                 cn: [
24397                 {
24398                     tag: 'table',
24399                     cls: 'table-condensed',
24400                     cn:[
24401                         {
24402                             tag: 'tbody',
24403                             cn: [
24404                                 {
24405                                     tag: 'tr',
24406                                     cn: [
24407                                     {
24408                                         tag: 'td',
24409                                         colspan: '7'
24410                                     }
24411                                     ]
24412                                 }
24413                             ]
24414                         },
24415                         {
24416                             tag: 'tfoot',
24417                             cn: [
24418                                 {
24419                                     tag: 'tr',
24420                                     cn: [
24421                                     {
24422                                         tag: 'th',
24423                                         colspan: '7',
24424                                         cls: '',
24425                                         cn: [
24426                                             {
24427                                                 tag: 'button',
24428                                                 cls: 'btn btn-info ok',
24429                                                 html: 'OK'
24430                                             }
24431                                         ]
24432                                     }
24433                     
24434                                     ]
24435                                 }
24436                             ]
24437                         }
24438                     ]
24439                 }
24440                 ]
24441             }
24442         ]
24443     }
24444 });
24445
24446  
24447
24448  /*
24449  * - LGPL
24450  *
24451  * MonthField
24452  * 
24453  */
24454
24455 /**
24456  * @class Roo.bootstrap.form.MonthField
24457  * @extends Roo.bootstrap.form.Input
24458  * Bootstrap MonthField class
24459  * 
24460  * @cfg {String} language default en
24461  * 
24462  * @constructor
24463  * Create a new MonthField
24464  * @param {Object} config The config object
24465  */
24466
24467 Roo.bootstrap.form.MonthField = function(config){
24468     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24469     
24470     this.addEvents({
24471         /**
24472          * @event show
24473          * Fires when this field show.
24474          * @param {Roo.bootstrap.form.MonthField} this
24475          * @param {Mixed} date The date value
24476          */
24477         show : true,
24478         /**
24479          * @event show
24480          * Fires when this field hide.
24481          * @param {Roo.bootstrap.form.MonthField} this
24482          * @param {Mixed} date The date value
24483          */
24484         hide : true,
24485         /**
24486          * @event select
24487          * Fires when select a date.
24488          * @param {Roo.bootstrap.form.MonthField} this
24489          * @param {String} oldvalue The old value
24490          * @param {String} newvalue The new value
24491          */
24492         select : true
24493     });
24494 };
24495
24496 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24497     
24498     onRender: function(ct, position)
24499     {
24500         
24501         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24502         
24503         this.language = this.language || 'en';
24504         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24505         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24506         
24507         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24508         this.isInline = false;
24509         this.isInput = true;
24510         this.component = this.el.select('.add-on', true).first() || false;
24511         this.component = (this.component && this.component.length === 0) ? false : this.component;
24512         this.hasInput = this.component && this.inputEL().length;
24513         
24514         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24515         
24516         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24517         
24518         this.picker().on('mousedown', this.onMousedown, this);
24519         this.picker().on('click', this.onClick, this);
24520         
24521         this.picker().addClass('datepicker-dropdown');
24522         
24523         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24524             v.setStyle('width', '189px');
24525         });
24526         
24527         this.fillMonths();
24528         
24529         this.update();
24530         
24531         if(this.isInline) {
24532             this.show();
24533         }
24534         
24535     },
24536     
24537     setValue: function(v, suppressEvent)
24538     {   
24539         var o = this.getValue();
24540         
24541         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24542         
24543         this.update();
24544
24545         if(suppressEvent !== true){
24546             this.fireEvent('select', this, o, v);
24547         }
24548         
24549     },
24550     
24551     getValue: function()
24552     {
24553         return this.value;
24554     },
24555     
24556     onClick: function(e) 
24557     {
24558         e.stopPropagation();
24559         e.preventDefault();
24560         
24561         var target = e.getTarget();
24562         
24563         if(target.nodeName.toLowerCase() === 'i'){
24564             target = Roo.get(target).dom.parentNode;
24565         }
24566         
24567         var nodeName = target.nodeName;
24568         var className = target.className;
24569         var html = target.innerHTML;
24570         
24571         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24572             return;
24573         }
24574         
24575         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24576         
24577         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24578         
24579         this.hide();
24580                         
24581     },
24582     
24583     picker : function()
24584     {
24585         return this.pickerEl;
24586     },
24587     
24588     fillMonths: function()
24589     {    
24590         var i = 0;
24591         var months = this.picker().select('>.datepicker-months td', true).first();
24592         
24593         months.dom.innerHTML = '';
24594         
24595         while (i < 12) {
24596             var month = {
24597                 tag: 'span',
24598                 cls: 'month',
24599                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24600             };
24601             
24602             months.createChild(month);
24603         }
24604         
24605     },
24606     
24607     update: function()
24608     {
24609         var _this = this;
24610         
24611         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24612             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24613         }
24614         
24615         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24616             e.removeClass('active');
24617             
24618             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24619                 e.addClass('active');
24620             }
24621         })
24622     },
24623     
24624     place: function()
24625     {
24626         if(this.isInline) {
24627             return;
24628         }
24629         
24630         this.picker().removeClass(['bottom', 'top']);
24631         
24632         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24633             /*
24634              * place to the top of element!
24635              *
24636              */
24637             
24638             this.picker().addClass('top');
24639             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24640             
24641             return;
24642         }
24643         
24644         this.picker().addClass('bottom');
24645         
24646         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24647     },
24648     
24649     onFocus : function()
24650     {
24651         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24652         this.show();
24653     },
24654     
24655     onBlur : function()
24656     {
24657         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24658         
24659         var d = this.inputEl().getValue();
24660         
24661         this.setValue(d);
24662                 
24663         this.hide();
24664     },
24665     
24666     show : function()
24667     {
24668         this.picker().show();
24669         this.picker().select('>.datepicker-months', true).first().show();
24670         this.update();
24671         this.place();
24672         
24673         this.fireEvent('show', this, this.date);
24674     },
24675     
24676     hide : function()
24677     {
24678         if(this.isInline) {
24679             return;
24680         }
24681         this.picker().hide();
24682         this.fireEvent('hide', this, this.date);
24683         
24684     },
24685     
24686     onMousedown: function(e)
24687     {
24688         e.stopPropagation();
24689         e.preventDefault();
24690     },
24691     
24692     keyup: function(e)
24693     {
24694         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24695         this.update();
24696     },
24697
24698     fireKey: function(e)
24699     {
24700         if (!this.picker().isVisible()){
24701             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24702                 this.show();
24703             }
24704             return;
24705         }
24706         
24707         var dir;
24708         
24709         switch(e.keyCode){
24710             case 27: // escape
24711                 this.hide();
24712                 e.preventDefault();
24713                 break;
24714             case 37: // left
24715             case 39: // right
24716                 dir = e.keyCode == 37 ? -1 : 1;
24717                 
24718                 this.vIndex = this.vIndex + dir;
24719                 
24720                 if(this.vIndex < 0){
24721                     this.vIndex = 0;
24722                 }
24723                 
24724                 if(this.vIndex > 11){
24725                     this.vIndex = 11;
24726                 }
24727                 
24728                 if(isNaN(this.vIndex)){
24729                     this.vIndex = 0;
24730                 }
24731                 
24732                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24733                 
24734                 break;
24735             case 38: // up
24736             case 40: // down
24737                 
24738                 dir = e.keyCode == 38 ? -1 : 1;
24739                 
24740                 this.vIndex = this.vIndex + dir * 4;
24741                 
24742                 if(this.vIndex < 0){
24743                     this.vIndex = 0;
24744                 }
24745                 
24746                 if(this.vIndex > 11){
24747                     this.vIndex = 11;
24748                 }
24749                 
24750                 if(isNaN(this.vIndex)){
24751                     this.vIndex = 0;
24752                 }
24753                 
24754                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24755                 break;
24756                 
24757             case 13: // enter
24758                 
24759                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24760                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24761                 }
24762                 
24763                 this.hide();
24764                 e.preventDefault();
24765                 break;
24766             case 9: // tab
24767                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24768                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24769                 }
24770                 this.hide();
24771                 break;
24772             case 16: // shift
24773             case 17: // ctrl
24774             case 18: // alt
24775                 break;
24776             default :
24777                 this.hide();
24778                 
24779         }
24780     },
24781     
24782     remove: function() 
24783     {
24784         this.picker().remove();
24785     }
24786    
24787 });
24788
24789 Roo.apply(Roo.bootstrap.form.MonthField,  {
24790     
24791     content : {
24792         tag: 'tbody',
24793         cn: [
24794         {
24795             tag: 'tr',
24796             cn: [
24797             {
24798                 tag: 'td',
24799                 colspan: '7'
24800             }
24801             ]
24802         }
24803         ]
24804     },
24805     
24806     dates:{
24807         en: {
24808             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24809             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24810         }
24811     }
24812 });
24813
24814 Roo.apply(Roo.bootstrap.form.MonthField,  {
24815   
24816     template : {
24817         tag: 'div',
24818         cls: 'datepicker dropdown-menu roo-dynamic',
24819         cn: [
24820             {
24821                 tag: 'div',
24822                 cls: 'datepicker-months',
24823                 cn: [
24824                 {
24825                     tag: 'table',
24826                     cls: 'table-condensed',
24827                     cn:[
24828                         Roo.bootstrap.form.DateField.content
24829                     ]
24830                 }
24831                 ]
24832             }
24833         ]
24834     }
24835 });
24836
24837  
24838
24839  
24840  /*
24841  * - LGPL
24842  *
24843  * CheckBox
24844  * 
24845  */
24846
24847 /**
24848  * @class Roo.bootstrap.form.CheckBox
24849  * @extends Roo.bootstrap.form.Input
24850  * Bootstrap CheckBox class
24851  * 
24852  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24853  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24854  * @cfg {String} boxLabel The text that appears beside the checkbox
24855  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24856  * @cfg {Boolean} checked initnal the element
24857  * @cfg {Boolean} inline inline the element (default false)
24858  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24859  * @cfg {String} tooltip label tooltip
24860  * 
24861  * @constructor
24862  * Create a new CheckBox
24863  * @param {Object} config The config object
24864  */
24865
24866 Roo.bootstrap.form.CheckBox = function(config){
24867     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24868    
24869     this.addEvents({
24870         /**
24871         * @event check
24872         * Fires when the element is checked or unchecked.
24873         * @param {Roo.bootstrap.form.CheckBox} this This input
24874         * @param {Boolean} checked The new checked value
24875         */
24876        check : true,
24877        /**
24878         * @event click
24879         * Fires when the element is click.
24880         * @param {Roo.bootstrap.form.CheckBox} this This input
24881         */
24882        click : true
24883     });
24884     
24885 };
24886
24887 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24888   
24889     inputType: 'checkbox',
24890     inputValue: 1,
24891     valueOff: 0,
24892     boxLabel: false,
24893     checked: false,
24894     weight : false,
24895     inline: false,
24896     tooltip : '',
24897     
24898     // checkbox success does not make any sense really.. 
24899     invalidClass : "",
24900     validClass : "",
24901     
24902     
24903     getAutoCreate : function()
24904     {
24905         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24906         
24907         var id = Roo.id();
24908         
24909         var cfg = {};
24910         
24911         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24912         
24913         if(this.inline){
24914             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24915         }
24916         
24917         var input =  {
24918             tag: 'input',
24919             id : id,
24920             type : this.inputType,
24921             value : this.inputValue,
24922             cls : 'roo-' + this.inputType, //'form-box',
24923             placeholder : this.placeholder || ''
24924             
24925         };
24926         
24927         if(this.inputType != 'radio'){
24928             var hidden =  {
24929                 tag: 'input',
24930                 type : 'hidden',
24931                 cls : 'roo-hidden-value',
24932                 value : this.checked ? this.inputValue : this.valueOff
24933             };
24934         }
24935         
24936             
24937         if (this.weight) { // Validity check?
24938             cfg.cls += " " + this.inputType + "-" + this.weight;
24939         }
24940         
24941         if (this.disabled) {
24942             input.disabled=true;
24943         }
24944         
24945         if(this.checked){
24946             input.checked = this.checked;
24947         }
24948         
24949         if (this.name) {
24950             
24951             input.name = this.name;
24952             
24953             if(this.inputType != 'radio'){
24954                 hidden.name = this.name;
24955                 input.name = '_hidden_' + this.name;
24956             }
24957         }
24958         
24959         if (this.size) {
24960             input.cls += ' input-' + this.size;
24961         }
24962         
24963         var settings=this;
24964         
24965         ['xs','sm','md','lg'].map(function(size){
24966             if (settings[size]) {
24967                 cfg.cls += ' col-' + size + '-' + settings[size];
24968             }
24969         });
24970         
24971         var inputblock = input;
24972          
24973         if (this.before || this.after) {
24974             
24975             inputblock = {
24976                 cls : 'input-group',
24977                 cn :  [] 
24978             };
24979             
24980             if (this.before) {
24981                 inputblock.cn.push({
24982                     tag :'span',
24983                     cls : 'input-group-addon',
24984                     html : this.before
24985                 });
24986             }
24987             
24988             inputblock.cn.push(input);
24989             
24990             if(this.inputType != 'radio'){
24991                 inputblock.cn.push(hidden);
24992             }
24993             
24994             if (this.after) {
24995                 inputblock.cn.push({
24996                     tag :'span',
24997                     cls : 'input-group-addon',
24998                     html : this.after
24999                 });
25000             }
25001             
25002         }
25003         var boxLabelCfg = false;
25004         
25005         if(this.boxLabel){
25006            
25007             boxLabelCfg = {
25008                 tag: 'label',
25009                 //'for': id, // box label is handled by onclick - so no for...
25010                 cls: 'box-label',
25011                 html: this.boxLabel
25012             };
25013             if(this.tooltip){
25014                 boxLabelCfg.tooltip = this.tooltip;
25015             }
25016              
25017         }
25018         
25019         
25020         if (align ==='left' && this.fieldLabel.length) {
25021 //                Roo.log("left and has label");
25022             cfg.cn = [
25023                 {
25024                     tag: 'label',
25025                     'for' :  id,
25026                     cls : 'control-label',
25027                     html : this.fieldLabel
25028                 },
25029                 {
25030                     cls : "", 
25031                     cn: [
25032                         inputblock
25033                     ]
25034                 }
25035             ];
25036             
25037             if (boxLabelCfg) {
25038                 cfg.cn[1].cn.push(boxLabelCfg);
25039             }
25040             
25041             if(this.labelWidth > 12){
25042                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25043             }
25044             
25045             if(this.labelWidth < 13 && this.labelmd == 0){
25046                 this.labelmd = this.labelWidth;
25047             }
25048             
25049             if(this.labellg > 0){
25050                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25051                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25052             }
25053             
25054             if(this.labelmd > 0){
25055                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25056                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25057             }
25058             
25059             if(this.labelsm > 0){
25060                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25061                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25062             }
25063             
25064             if(this.labelxs > 0){
25065                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25066                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25067             }
25068             
25069         } else if ( this.fieldLabel.length) {
25070 //                Roo.log(" label");
25071                 cfg.cn = [
25072                    
25073                     {
25074                         tag: this.boxLabel ? 'span' : 'label',
25075                         'for': id,
25076                         cls: 'control-label box-input-label',
25077                         //cls : 'input-group-addon',
25078                         html : this.fieldLabel
25079                     },
25080                     
25081                     inputblock
25082                     
25083                 ];
25084                 if (boxLabelCfg) {
25085                     cfg.cn.push(boxLabelCfg);
25086                 }
25087
25088         } else {
25089             
25090 //                Roo.log(" no label && no align");
25091                 cfg.cn = [  inputblock ] ;
25092                 if (boxLabelCfg) {
25093                     cfg.cn.push(boxLabelCfg);
25094                 }
25095
25096                 
25097         }
25098         
25099        
25100         
25101         if(this.inputType != 'radio'){
25102             cfg.cn.push(hidden);
25103         }
25104         
25105         return cfg;
25106         
25107     },
25108     
25109     /**
25110      * return the real input element.
25111      */
25112     inputEl: function ()
25113     {
25114         return this.el.select('input.roo-' + this.inputType,true).first();
25115     },
25116     hiddenEl: function ()
25117     {
25118         return this.el.select('input.roo-hidden-value',true).first();
25119     },
25120     
25121     labelEl: function()
25122     {
25123         return this.el.select('label.control-label',true).first();
25124     },
25125     /* depricated... */
25126     
25127     label: function()
25128     {
25129         return this.labelEl();
25130     },
25131     
25132     boxLabelEl: function()
25133     {
25134         return this.el.select('label.box-label',true).first();
25135     },
25136     
25137     initEvents : function()
25138     {
25139 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25140         
25141         this.inputEl().on('click', this.onClick,  this);
25142         
25143         if (this.boxLabel) { 
25144             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25145         }
25146         
25147         this.startValue = this.getValue();
25148         
25149         if(this.groupId){
25150             Roo.bootstrap.form.CheckBox.register(this);
25151         }
25152     },
25153     
25154     onClick : function(e)
25155     {   
25156         if(this.fireEvent('click', this, e) !== false){
25157             this.setChecked(!this.checked);
25158         }
25159         
25160     },
25161     
25162     setChecked : function(state,suppressEvent)
25163     {
25164         this.startValue = this.getValue();
25165
25166         if(this.inputType == 'radio'){
25167             
25168             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25169                 e.dom.checked = false;
25170             });
25171             
25172             this.inputEl().dom.checked = true;
25173             
25174             this.inputEl().dom.value = this.inputValue;
25175             
25176             if(suppressEvent !== true){
25177                 this.fireEvent('check', this, true);
25178             }
25179             
25180             this.validate();
25181             
25182             return;
25183         }
25184         
25185         this.checked = state;
25186         
25187         this.inputEl().dom.checked = state;
25188         
25189         
25190         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25191         
25192         if(suppressEvent !== true){
25193             this.fireEvent('check', this, state);
25194         }
25195         
25196         this.validate();
25197     },
25198     
25199     getValue : function()
25200     {
25201         if(this.inputType == 'radio'){
25202             return this.getGroupValue();
25203         }
25204         
25205         return this.hiddenEl().dom.value;
25206         
25207     },
25208     
25209     getGroupValue : function()
25210     {
25211         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25212             return '';
25213         }
25214         
25215         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25216     },
25217     
25218     setValue : function(v,suppressEvent)
25219     {
25220         if(this.inputType == 'radio'){
25221             this.setGroupValue(v, suppressEvent);
25222             return;
25223         }
25224         
25225         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25226         
25227         this.validate();
25228     },
25229     
25230     setGroupValue : function(v, suppressEvent)
25231     {
25232         this.startValue = this.getValue();
25233         
25234         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25235             e.dom.checked = false;
25236             
25237             if(e.dom.value == v){
25238                 e.dom.checked = true;
25239             }
25240         });
25241         
25242         if(suppressEvent !== true){
25243             this.fireEvent('check', this, true);
25244         }
25245
25246         this.validate();
25247         
25248         return;
25249     },
25250     
25251     validate : function()
25252     {
25253         if(this.getVisibilityEl().hasClass('hidden')){
25254             return true;
25255         }
25256         
25257         if(
25258                 this.disabled || 
25259                 (this.inputType == 'radio' && this.validateRadio()) ||
25260                 (this.inputType == 'checkbox' && this.validateCheckbox())
25261         ){
25262             this.markValid();
25263             return true;
25264         }
25265         
25266         this.markInvalid();
25267         return false;
25268     },
25269     
25270     validateRadio : function()
25271     {
25272         if(this.getVisibilityEl().hasClass('hidden')){
25273             return true;
25274         }
25275         
25276         if(this.allowBlank){
25277             return true;
25278         }
25279         
25280         var valid = false;
25281         
25282         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25283             if(!e.dom.checked){
25284                 return;
25285             }
25286             
25287             valid = true;
25288             
25289             return false;
25290         });
25291         
25292         return valid;
25293     },
25294     
25295     validateCheckbox : function()
25296     {
25297         if(!this.groupId){
25298             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25299             //return (this.getValue() == this.inputValue) ? true : false;
25300         }
25301         
25302         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25303         
25304         if(!group){
25305             return false;
25306         }
25307         
25308         var r = false;
25309         
25310         for(var i in group){
25311             if(group[i].el.isVisible(true)){
25312                 r = false;
25313                 break;
25314             }
25315             
25316             r = true;
25317         }
25318         
25319         for(var i in group){
25320             if(r){
25321                 break;
25322             }
25323             
25324             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25325         }
25326         
25327         return r;
25328     },
25329     
25330     /**
25331      * Mark this field as valid
25332      */
25333     markValid : function()
25334     {
25335         var _this = this;
25336         
25337         this.fireEvent('valid', this);
25338         
25339         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25340         
25341         if(this.groupId){
25342             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25343         }
25344         
25345         if(label){
25346             label.markValid();
25347         }
25348
25349         if(this.inputType == 'radio'){
25350             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25351                 var fg = e.findParent('.form-group', false, true);
25352                 if (Roo.bootstrap.version == 3) {
25353                     fg.removeClass([_this.invalidClass, _this.validClass]);
25354                     fg.addClass(_this.validClass);
25355                 } else {
25356                     fg.removeClass(['is-valid', 'is-invalid']);
25357                     fg.addClass('is-valid');
25358                 }
25359             });
25360             
25361             return;
25362         }
25363
25364         if(!this.groupId){
25365             var fg = this.el.findParent('.form-group', false, true);
25366             if (Roo.bootstrap.version == 3) {
25367                 fg.removeClass([this.invalidClass, this.validClass]);
25368                 fg.addClass(this.validClass);
25369             } else {
25370                 fg.removeClass(['is-valid', 'is-invalid']);
25371                 fg.addClass('is-valid');
25372             }
25373             return;
25374         }
25375         
25376         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25377         
25378         if(!group){
25379             return;
25380         }
25381         
25382         for(var i in group){
25383             var fg = group[i].el.findParent('.form-group', false, true);
25384             if (Roo.bootstrap.version == 3) {
25385                 fg.removeClass([this.invalidClass, this.validClass]);
25386                 fg.addClass(this.validClass);
25387             } else {
25388                 fg.removeClass(['is-valid', 'is-invalid']);
25389                 fg.addClass('is-valid');
25390             }
25391         }
25392     },
25393     
25394      /**
25395      * Mark this field as invalid
25396      * @param {String} msg The validation message
25397      */
25398     markInvalid : function(msg)
25399     {
25400         if(this.allowBlank){
25401             return;
25402         }
25403         
25404         var _this = this;
25405         
25406         this.fireEvent('invalid', this, msg);
25407         
25408         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25409         
25410         if(this.groupId){
25411             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25412         }
25413         
25414         if(label){
25415             label.markInvalid();
25416         }
25417             
25418         if(this.inputType == 'radio'){
25419             
25420             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25421                 var fg = e.findParent('.form-group', false, true);
25422                 if (Roo.bootstrap.version == 3) {
25423                     fg.removeClass([_this.invalidClass, _this.validClass]);
25424                     fg.addClass(_this.invalidClass);
25425                 } else {
25426                     fg.removeClass(['is-invalid', 'is-valid']);
25427                     fg.addClass('is-invalid');
25428                 }
25429             });
25430             
25431             return;
25432         }
25433         
25434         if(!this.groupId){
25435             var fg = this.el.findParent('.form-group', false, true);
25436             if (Roo.bootstrap.version == 3) {
25437                 fg.removeClass([_this.invalidClass, _this.validClass]);
25438                 fg.addClass(_this.invalidClass);
25439             } else {
25440                 fg.removeClass(['is-invalid', 'is-valid']);
25441                 fg.addClass('is-invalid');
25442             }
25443             return;
25444         }
25445         
25446         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25447         
25448         if(!group){
25449             return;
25450         }
25451         
25452         for(var i in group){
25453             var fg = group[i].el.findParent('.form-group', false, true);
25454             if (Roo.bootstrap.version == 3) {
25455                 fg.removeClass([_this.invalidClass, _this.validClass]);
25456                 fg.addClass(_this.invalidClass);
25457             } else {
25458                 fg.removeClass(['is-invalid', 'is-valid']);
25459                 fg.addClass('is-invalid');
25460             }
25461         }
25462         
25463     },
25464     
25465     clearInvalid : function()
25466     {
25467         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25468         
25469         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25470         
25471         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25472         
25473         if (label && label.iconEl) {
25474             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25475             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25476         }
25477     },
25478     
25479     disable : function()
25480     {
25481         if(this.inputType != 'radio'){
25482             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25483             return;
25484         }
25485         
25486         var _this = this;
25487         
25488         if(this.rendered){
25489             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25490                 _this.getActionEl().addClass(this.disabledClass);
25491                 e.dom.disabled = true;
25492             });
25493         }
25494         
25495         this.disabled = true;
25496         this.fireEvent("disable", this);
25497         return this;
25498     },
25499
25500     enable : function()
25501     {
25502         if(this.inputType != 'radio'){
25503             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25504             return;
25505         }
25506         
25507         var _this = this;
25508         
25509         if(this.rendered){
25510             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25511                 _this.getActionEl().removeClass(this.disabledClass);
25512                 e.dom.disabled = false;
25513             });
25514         }
25515         
25516         this.disabled = false;
25517         this.fireEvent("enable", this);
25518         return this;
25519     },
25520     
25521     setBoxLabel : function(v)
25522     {
25523         this.boxLabel = v;
25524         
25525         if(this.rendered){
25526             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25527         }
25528     }
25529
25530 });
25531
25532 Roo.apply(Roo.bootstrap.form.CheckBox, {
25533     
25534     groups: {},
25535     
25536      /**
25537     * register a CheckBox Group
25538     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25539     */
25540     register : function(checkbox)
25541     {
25542         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25543             this.groups[checkbox.groupId] = {};
25544         }
25545         
25546         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25547             return;
25548         }
25549         
25550         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25551         
25552     },
25553     /**
25554     * fetch a CheckBox Group based on the group ID
25555     * @param {string} the group ID
25556     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25557     */
25558     get: function(groupId) {
25559         if (typeof(this.groups[groupId]) == 'undefined') {
25560             return false;
25561         }
25562         
25563         return this.groups[groupId] ;
25564     }
25565     
25566     
25567 });
25568 /*
25569  * - LGPL
25570  *
25571  * RadioItem
25572  * 
25573  */
25574
25575 /**
25576  * @class Roo.bootstrap.form.Radio
25577  * @extends Roo.bootstrap.Component
25578  * Bootstrap Radio class
25579  * @cfg {String} boxLabel - the label associated
25580  * @cfg {String} value - the value of radio
25581  * 
25582  * @constructor
25583  * Create a new Radio
25584  * @param {Object} config The config object
25585  */
25586 Roo.bootstrap.form.Radio = function(config){
25587     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25588     
25589 };
25590
25591 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25592     
25593     boxLabel : '',
25594     
25595     value : '',
25596     
25597     getAutoCreate : function()
25598     {
25599         var cfg = {
25600             tag : 'div',
25601             cls : 'form-group radio',
25602             cn : [
25603                 {
25604                     tag : 'label',
25605                     cls : 'box-label',
25606                     html : this.boxLabel
25607                 }
25608             ]
25609         };
25610         
25611         return cfg;
25612     },
25613     
25614     initEvents : function() 
25615     {
25616         this.parent().register(this);
25617         
25618         this.el.on('click', this.onClick, this);
25619         
25620     },
25621     
25622     onClick : function(e)
25623     {
25624         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25625             this.setChecked(true);
25626         }
25627     },
25628     
25629     setChecked : function(state, suppressEvent)
25630     {
25631         this.parent().setValue(this.value, suppressEvent);
25632         
25633     },
25634     
25635     setBoxLabel : function(v)
25636     {
25637         this.boxLabel = v;
25638         
25639         if(this.rendered){
25640             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25641         }
25642     }
25643     
25644 });
25645  
25646
25647  /*
25648  * - LGPL
25649  *
25650  * Input
25651  * 
25652  */
25653
25654 /**
25655  * @class Roo.bootstrap.form.SecurePass
25656  * @extends Roo.bootstrap.form.Input
25657  * Bootstrap SecurePass class
25658  *
25659  * 
25660  * @constructor
25661  * Create a new SecurePass
25662  * @param {Object} config The config object
25663  */
25664  
25665 Roo.bootstrap.form.SecurePass = function (config) {
25666     // these go here, so the translation tool can replace them..
25667     this.errors = {
25668         PwdEmpty: "Please type a password, and then retype it to confirm.",
25669         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25670         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25671         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25672         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25673         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25674         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25675         TooWeak: "Your password is Too Weak."
25676     },
25677     this.meterLabel = "Password strength:";
25678     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25679     this.meterClass = [
25680         "roo-password-meter-tooweak", 
25681         "roo-password-meter-weak", 
25682         "roo-password-meter-medium", 
25683         "roo-password-meter-strong", 
25684         "roo-password-meter-grey"
25685     ];
25686     
25687     this.errors = {};
25688     
25689     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25690 }
25691
25692 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25693     /**
25694      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25695      * {
25696      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25697      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25698      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25699      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25700      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25701      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25702      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25703      * })
25704      */
25705     // private
25706     
25707     meterWidth: 300,
25708     errorMsg :'',    
25709     errors: false,
25710     imageRoot: '/',
25711     /**
25712      * @cfg {String/Object} Label for the strength meter (defaults to
25713      * 'Password strength:')
25714      */
25715     // private
25716     meterLabel: '',
25717     /**
25718      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25719      * ['Weak', 'Medium', 'Strong'])
25720      */
25721     // private    
25722     pwdStrengths: false,    
25723     // private
25724     strength: 0,
25725     // private
25726     _lastPwd: null,
25727     // private
25728     kCapitalLetter: 0,
25729     kSmallLetter: 1,
25730     kDigit: 2,
25731     kPunctuation: 3,
25732     
25733     insecure: false,
25734     // private
25735     initEvents: function ()
25736     {
25737         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25738
25739         if (this.el.is('input[type=password]') && Roo.isSafari) {
25740             this.el.on('keydown', this.SafariOnKeyDown, this);
25741         }
25742
25743         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25744     },
25745     // private
25746     onRender: function (ct, position)
25747     {
25748         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25749         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25750         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25751
25752         this.trigger.createChild({
25753                    cn: [
25754                     {
25755                     //id: 'PwdMeter',
25756                     tag: 'div',
25757                     cls: 'roo-password-meter-grey col-xs-12',
25758                     style: {
25759                         //width: 0,
25760                         //width: this.meterWidth + 'px'                                                
25761                         }
25762                     },
25763                     {                            
25764                          cls: 'roo-password-meter-text'                          
25765                     }
25766                 ]            
25767         });
25768
25769          
25770         if (this.hideTrigger) {
25771             this.trigger.setDisplayed(false);
25772         }
25773         this.setSize(this.width || '', this.height || '');
25774     },
25775     // private
25776     onDestroy: function ()
25777     {
25778         if (this.trigger) {
25779             this.trigger.removeAllListeners();
25780             this.trigger.remove();
25781         }
25782         if (this.wrap) {
25783             this.wrap.remove();
25784         }
25785         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25786     },
25787     // private
25788     checkStrength: function ()
25789     {
25790         var pwd = this.inputEl().getValue();
25791         if (pwd == this._lastPwd) {
25792             return;
25793         }
25794
25795         var strength;
25796         if (this.ClientSideStrongPassword(pwd)) {
25797             strength = 3;
25798         } else if (this.ClientSideMediumPassword(pwd)) {
25799             strength = 2;
25800         } else if (this.ClientSideWeakPassword(pwd)) {
25801             strength = 1;
25802         } else {
25803             strength = 0;
25804         }
25805         
25806         Roo.log('strength1: ' + strength);
25807         
25808         //var pm = this.trigger.child('div/div/div').dom;
25809         var pm = this.trigger.child('div/div');
25810         pm.removeClass(this.meterClass);
25811         pm.addClass(this.meterClass[strength]);
25812                 
25813         
25814         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25815                 
25816         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25817         
25818         this._lastPwd = pwd;
25819     },
25820     reset: function ()
25821     {
25822         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25823         
25824         this._lastPwd = '';
25825         
25826         var pm = this.trigger.child('div/div');
25827         pm.removeClass(this.meterClass);
25828         pm.addClass('roo-password-meter-grey');        
25829         
25830         
25831         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25832         
25833         pt.innerHTML = '';
25834         this.inputEl().dom.type='password';
25835     },
25836     // private
25837     validateValue: function (value)
25838     {
25839         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25840             return false;
25841         }
25842         if (value.length == 0) {
25843             if (this.allowBlank) {
25844                 this.clearInvalid();
25845                 return true;
25846             }
25847
25848             this.markInvalid(this.errors.PwdEmpty);
25849             this.errorMsg = this.errors.PwdEmpty;
25850             return false;
25851         }
25852         
25853         if(this.insecure){
25854             return true;
25855         }
25856         
25857         if (!value.match(/[\x21-\x7e]+/)) {
25858             this.markInvalid(this.errors.PwdBadChar);
25859             this.errorMsg = this.errors.PwdBadChar;
25860             return false;
25861         }
25862         if (value.length < 6) {
25863             this.markInvalid(this.errors.PwdShort);
25864             this.errorMsg = this.errors.PwdShort;
25865             return false;
25866         }
25867         if (value.length > 16) {
25868             this.markInvalid(this.errors.PwdLong);
25869             this.errorMsg = this.errors.PwdLong;
25870             return false;
25871         }
25872         var strength;
25873         if (this.ClientSideStrongPassword(value)) {
25874             strength = 3;
25875         } else if (this.ClientSideMediumPassword(value)) {
25876             strength = 2;
25877         } else if (this.ClientSideWeakPassword(value)) {
25878             strength = 1;
25879         } else {
25880             strength = 0;
25881         }
25882
25883         
25884         if (strength < 2) {
25885             //this.markInvalid(this.errors.TooWeak);
25886             this.errorMsg = this.errors.TooWeak;
25887             //return false;
25888         }
25889         
25890         
25891         console.log('strength2: ' + strength);
25892         
25893         //var pm = this.trigger.child('div/div/div').dom;
25894         
25895         var pm = this.trigger.child('div/div');
25896         pm.removeClass(this.meterClass);
25897         pm.addClass(this.meterClass[strength]);
25898                 
25899         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25900                 
25901         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25902         
25903         this.errorMsg = ''; 
25904         return true;
25905     },
25906     // private
25907     CharacterSetChecks: function (type)
25908     {
25909         this.type = type;
25910         this.fResult = false;
25911     },
25912     // private
25913     isctype: function (character, type)
25914     {
25915         switch (type) {  
25916             case this.kCapitalLetter:
25917                 if (character >= 'A' && character <= 'Z') {
25918                     return true;
25919                 }
25920                 break;
25921             
25922             case this.kSmallLetter:
25923                 if (character >= 'a' && character <= 'z') {
25924                     return true;
25925                 }
25926                 break;
25927             
25928             case this.kDigit:
25929                 if (character >= '0' && character <= '9') {
25930                     return true;
25931                 }
25932                 break;
25933             
25934             case this.kPunctuation:
25935                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25936                     return true;
25937                 }
25938                 break;
25939             
25940             default:
25941                 return false;
25942         }
25943
25944     },
25945     // private
25946     IsLongEnough: function (pwd, size)
25947     {
25948         return !(pwd == null || isNaN(size) || pwd.length < size);
25949     },
25950     // private
25951     SpansEnoughCharacterSets: function (word, nb)
25952     {
25953         if (!this.IsLongEnough(word, nb))
25954         {
25955             return false;
25956         }
25957
25958         var characterSetChecks = new Array(
25959             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25960             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25961         );
25962         
25963         for (var index = 0; index < word.length; ++index) {
25964             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25965                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25966                     characterSetChecks[nCharSet].fResult = true;
25967                     break;
25968                 }
25969             }
25970         }
25971
25972         var nCharSets = 0;
25973         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25974             if (characterSetChecks[nCharSet].fResult) {
25975                 ++nCharSets;
25976             }
25977         }
25978
25979         if (nCharSets < nb) {
25980             return false;
25981         }
25982         return true;
25983     },
25984     // private
25985     ClientSideStrongPassword: function (pwd)
25986     {
25987         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25988     },
25989     // private
25990     ClientSideMediumPassword: function (pwd)
25991     {
25992         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25993     },
25994     // private
25995     ClientSideWeakPassword: function (pwd)
25996     {
25997         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25998     }
25999           
26000 });Roo.rtf = {}; // namespace
26001 Roo.rtf.Hex = function(hex)
26002 {
26003     this.hexstr = hex;
26004 };
26005 Roo.rtf.Paragraph = function(opts)
26006 {
26007     this.content = []; ///??? is that used?
26008 };Roo.rtf.Span = function(opts)
26009 {
26010     this.value = opts.value;
26011 };
26012
26013 Roo.rtf.Group = function(parent)
26014 {
26015     // we dont want to acutally store parent - it will make debug a nightmare..
26016     this.content = [];
26017     this.cn  = [];
26018      
26019        
26020     
26021 };
26022
26023 Roo.rtf.Group.prototype = {
26024     ignorable : false,
26025     content: false,
26026     cn: false,
26027     addContent : function(node) {
26028         // could set styles...
26029         this.content.push(node);
26030     },
26031     addChild : function(cn)
26032     {
26033         this.cn.push(cn);
26034     },
26035     // only for images really...
26036     toDataURL : function()
26037     {
26038         var mimetype = false;
26039         switch(true) {
26040             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
26041                 mimetype = "image/png";
26042                 break;
26043              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26044                 mimetype = "image/jpeg";
26045                 break;
26046             default :
26047                 return 'about:blank'; // ?? error?
26048         }
26049         
26050         
26051         var hexstring = this.content[this.content.length-1].value;
26052         
26053         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26054             return String.fromCharCode(parseInt(a, 16));
26055         }).join(""));
26056     }
26057     
26058 };
26059 // this looks like it's normally the {rtf{ .... }}
26060 Roo.rtf.Document = function()
26061 {
26062     // we dont want to acutally store parent - it will make debug a nightmare..
26063     this.rtlch  = [];
26064     this.content = [];
26065     this.cn = [];
26066     
26067 };
26068 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
26069     addChild : function(cn)
26070     {
26071         this.cn.push(cn);
26072         switch(cn.type) {
26073             case 'rtlch': // most content seems to be inside this??
26074             case 'listtext':
26075             case 'shpinst':
26076                 this.rtlch.push(cn);
26077                 return;
26078             default:
26079                 this[cn.type] = cn;
26080         }
26081         
26082     },
26083     
26084     getElementsByType : function(type)
26085     {
26086         var ret =  [];
26087         this._getElementsByType(type, ret, this.cn, 'rtf');
26088         return ret;
26089     },
26090     _getElementsByType : function (type, ret, search_array, path)
26091     {
26092         search_array.forEach(function(n,i) {
26093             if (n.type == type) {
26094                 n.path = path + '/' + n.type + ':' + i;
26095                 ret.push(n);
26096             }
26097             if (n.cn.length > 0) {
26098                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26099             }
26100         },this);
26101     }
26102     
26103 });
26104  
26105 Roo.rtf.Ctrl = function(opts)
26106 {
26107     this.value = opts.value;
26108     this.param = opts.param;
26109 };
26110 /**
26111  *
26112  *
26113  * based on this https://github.com/iarna/rtf-parser
26114  * it's really only designed to extract pict from pasted RTF 
26115  *
26116  * usage:
26117  *
26118  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26119  *  
26120  *
26121  */
26122
26123  
26124
26125
26126
26127 Roo.rtf.Parser = function(text) {
26128     //super({objectMode: true})
26129     this.text = '';
26130     this.parserState = this.parseText;
26131     
26132     // these are for interpeter...
26133     this.doc = {};
26134     ///this.parserState = this.parseTop
26135     this.groupStack = [];
26136     this.hexStore = [];
26137     this.doc = false;
26138     
26139     this.groups = []; // where we put the return.
26140     
26141     for (var ii = 0; ii < text.length; ++ii) {
26142         ++this.cpos;
26143         
26144         if (text[ii] === '\n') {
26145             ++this.row;
26146             this.col = 1;
26147         } else {
26148             ++this.col;
26149         }
26150         this.parserState(text[ii]);
26151     }
26152     
26153     
26154     
26155 };
26156 Roo.rtf.Parser.prototype = {
26157     text : '', // string being parsed..
26158     controlWord : '',
26159     controlWordParam :  '',
26160     hexChar : '',
26161     doc : false,
26162     group: false,
26163     groupStack : false,
26164     hexStore : false,
26165     
26166     
26167     cpos : 0, 
26168     row : 1, // reportin?
26169     col : 1, //
26170
26171      
26172     push : function (el)
26173     {
26174         var m = 'cmd'+ el.type;
26175         if (typeof(this[m]) == 'undefined') {
26176             Roo.log('invalid cmd:' + el.type);
26177             return;
26178         }
26179         this[m](el);
26180         //Roo.log(el);
26181     },
26182     flushHexStore : function()
26183     {
26184         if (this.hexStore.length < 1) {
26185             return;
26186         }
26187         var hexstr = this.hexStore.map(
26188             function(cmd) {
26189                 return cmd.value;
26190         }).join('');
26191         
26192         this.group.addContent( new Roo.rtf.Hex( hexstr ));
26193               
26194             
26195         this.hexStore.splice(0)
26196         
26197     },
26198     
26199     cmdgroupstart : function()
26200     {
26201         this.flushHexStore();
26202         if (this.group) {
26203             this.groupStack.push(this.group);
26204         }
26205          // parent..
26206         if (this.doc === false) {
26207             this.group = this.doc = new Roo.rtf.Document();
26208             return;
26209             
26210         }
26211         this.group = new Roo.rtf.Group(this.group);
26212     },
26213     cmdignorable : function()
26214     {
26215         this.flushHexStore();
26216         this.group.ignorable = true;
26217     },
26218     cmdendparagraph : function()
26219     {
26220         this.flushHexStore();
26221         this.group.addContent(new Roo.rtf.Paragraph());
26222     },
26223     cmdgroupend : function ()
26224     {
26225         this.flushHexStore();
26226         var endingGroup = this.group;
26227         
26228         
26229         this.group = this.groupStack.pop();
26230         if (this.group) {
26231             this.group.addChild(endingGroup);
26232         }
26233         
26234         
26235         
26236         var doc = this.group || this.doc;
26237         //if (endingGroup instanceof FontTable) {
26238         //  doc.fonts = endingGroup.table
26239         //} else if (endingGroup instanceof ColorTable) {
26240         //  doc.colors = endingGroup.table
26241         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26242         if (endingGroup.ignorable === false) {
26243             //code
26244             this.groups.push(endingGroup);
26245            // Roo.log( endingGroup );
26246         }
26247             //Roo.each(endingGroup.content, function(item)) {
26248             //    doc.addContent(item);
26249             //}
26250             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26251         //}
26252     },
26253     cmdtext : function (cmd)
26254     {
26255         this.flushHexStore();
26256         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26257             //this.group = this.doc
26258             return;  // we really don't care about stray text...
26259         }
26260         this.group.addContent(new Roo.rtf.Span(cmd));
26261     },
26262     cmdcontrolword : function (cmd)
26263     {
26264         this.flushHexStore();
26265         if (!this.group.type) {
26266             this.group.type = cmd.value;
26267             return;
26268         }
26269         this.group.addContent(new Roo.rtf.Ctrl(cmd));
26270         // we actually don't care about ctrl words...
26271         return ;
26272         /*
26273         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26274         if (this[method]) {
26275             this[method](cmd.param)
26276         } else {
26277             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26278         }
26279         */
26280     },
26281     cmdhexchar : function(cmd) {
26282         this.hexStore.push(cmd);
26283     },
26284     cmderror : function(cmd) {
26285         throw cmd.value;
26286     },
26287     
26288     /*
26289       _flush (done) {
26290         if (this.text !== '\u0000') this.emitText()
26291         done()
26292       }
26293       */
26294       
26295       
26296     parseText : function(c)
26297     {
26298         if (c === '\\') {
26299             this.parserState = this.parseEscapes;
26300         } else if (c === '{') {
26301             this.emitStartGroup();
26302         } else if (c === '}') {
26303             this.emitEndGroup();
26304         } else if (c === '\x0A' || c === '\x0D') {
26305             // cr/lf are noise chars
26306         } else {
26307             this.text += c;
26308         }
26309     },
26310     
26311     parseEscapes: function (c)
26312     {
26313         if (c === '\\' || c === '{' || c === '}') {
26314             this.text += c;
26315             this.parserState = this.parseText;
26316         } else {
26317             this.parserState = this.parseControlSymbol;
26318             this.parseControlSymbol(c);
26319         }
26320     },
26321     parseControlSymbol: function(c)
26322     {
26323         if (c === '~') {
26324             this.text += '\u00a0'; // nbsp
26325             this.parserState = this.parseText
26326         } else if (c === '-') {
26327              this.text += '\u00ad'; // soft hyphen
26328         } else if (c === '_') {
26329             this.text += '\u2011'; // non-breaking hyphen
26330         } else if (c === '*') {
26331             this.emitIgnorable();
26332             this.parserState = this.parseText;
26333         } else if (c === "'") {
26334             this.parserState = this.parseHexChar;
26335         } else if (c === '|') { // formula cacter
26336             this.emitFormula();
26337             this.parserState = this.parseText;
26338         } else if (c === ':') { // subentry in an index entry
26339             this.emitIndexSubEntry();
26340             this.parserState = this.parseText;
26341         } else if (c === '\x0a') {
26342             this.emitEndParagraph();
26343             this.parserState = this.parseText;
26344         } else if (c === '\x0d') {
26345             this.emitEndParagraph();
26346             this.parserState = this.parseText;
26347         } else {
26348             this.parserState = this.parseControlWord;
26349             this.parseControlWord(c);
26350         }
26351     },
26352     parseHexChar: function (c)
26353     {
26354         if (/^[A-Fa-f0-9]$/.test(c)) {
26355             this.hexChar += c;
26356             if (this.hexChar.length >= 2) {
26357               this.emitHexChar();
26358               this.parserState = this.parseText;
26359             }
26360             return;
26361         }
26362         this.emitError("Invalid character \"" + c + "\" in hex literal.");
26363         this.parserState = this.parseText;
26364         
26365     },
26366     parseControlWord : function(c)
26367     {
26368         if (c === ' ') {
26369             this.emitControlWord();
26370             this.parserState = this.parseText;
26371         } else if (/^[-\d]$/.test(c)) {
26372             this.parserState = this.parseControlWordParam;
26373             this.controlWordParam += c;
26374         } else if (/^[A-Za-z]$/.test(c)) {
26375           this.controlWord += c;
26376         } else {
26377           this.emitControlWord();
26378           this.parserState = this.parseText;
26379           this.parseText(c);
26380         }
26381     },
26382     parseControlWordParam : function (c) {
26383         if (/^\d$/.test(c)) {
26384           this.controlWordParam += c;
26385         } else if (c === ' ') {
26386           this.emitControlWord();
26387           this.parserState = this.parseText;
26388         } else {
26389           this.emitControlWord();
26390           this.parserState = this.parseText;
26391           this.parseText(c);
26392         }
26393     },
26394     
26395     
26396     
26397     
26398     emitText : function () {
26399         if (this.text === '') {
26400             return;
26401         }
26402         this.push({
26403             type: 'text',
26404             value: this.text,
26405             pos: this.cpos,
26406             row: this.row,
26407             col: this.col
26408         });
26409         this.text = ''
26410     },
26411     emitControlWord : function ()
26412     {
26413         this.emitText();
26414         if (this.controlWord === '') {
26415             // do we want to track this - it seems just to cause problems.
26416             //this.emitError('empty control word');
26417         } else {
26418             this.push({
26419                   type: 'controlword',
26420                   value: this.controlWord,
26421                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
26422                   pos: this.cpos,
26423                   row: this.row,
26424                   col: this.col
26425             });
26426         }
26427         this.controlWord = '';
26428         this.controlWordParam = '';
26429     },
26430     emitStartGroup : function ()
26431     {
26432         this.emitText();
26433         this.push({
26434             type: 'groupstart',
26435             pos: this.cpos,
26436             row: this.row,
26437             col: this.col
26438         });
26439     },
26440     emitEndGroup : function ()
26441     {
26442         this.emitText();
26443         this.push({
26444             type: 'groupend',
26445             pos: this.cpos,
26446             row: this.row,
26447             col: this.col
26448         });
26449     },
26450     emitIgnorable : function ()
26451     {
26452         this.emitText();
26453         this.push({
26454             type: 'ignorable',
26455             pos: this.cpos,
26456             row: this.row,
26457             col: this.col
26458         });
26459     },
26460     emitHexChar : function ()
26461     {
26462         this.emitText();
26463         this.push({
26464             type: 'hexchar',
26465             value: this.hexChar,
26466             pos: this.cpos,
26467             row: this.row,
26468             col: this.col
26469         });
26470         this.hexChar = ''
26471     },
26472     emitError : function (message)
26473     {
26474       this.emitText();
26475       this.push({
26476             type: 'error',
26477             value: message,
26478             row: this.row,
26479             col: this.col,
26480             char: this.cpos //,
26481             //stack: new Error().stack
26482         });
26483     },
26484     emitEndParagraph : function () {
26485         this.emitText();
26486         this.push({
26487             type: 'endparagraph',
26488             pos: this.cpos,
26489             row: this.row,
26490             col: this.col
26491         });
26492     }
26493      
26494 } ;
26495 Roo.htmleditor = {};
26496  
26497 /**
26498  * @class Roo.htmleditor.Filter
26499  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26500  * @cfg {DomElement} node The node to iterate and filter
26501  * @cfg {boolean|String|Array} tag Tags to replace 
26502  * @constructor
26503  * Create a new Filter.
26504  * @param {Object} config Configuration options
26505  */
26506
26507
26508
26509 Roo.htmleditor.Filter = function(cfg) {
26510     Roo.apply(this.cfg);
26511     // this does not actually call walk as it's really just a abstract class
26512 }
26513
26514
26515 Roo.htmleditor.Filter.prototype = {
26516     
26517     node: false,
26518     
26519     tag: false,
26520
26521     // overrride to do replace comments.
26522     replaceComment : false,
26523     
26524     // overrride to do replace or do stuff with tags..
26525     replaceTag : false,
26526     
26527     walk : function(dom)
26528     {
26529         Roo.each( Array.from(dom.childNodes), function( e ) {
26530             switch(true) {
26531                 
26532                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26533                     this.replaceComment(e);
26534                     return;
26535                 
26536                 case e.nodeType != 1: //not a node.
26537                     return;
26538                 
26539                 case this.tag === true: // everything
26540                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26541                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26542                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26543                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26544                     if (this.replaceTag && false === this.replaceTag(e)) {
26545                         return;
26546                     }
26547                     if (e.hasChildNodes()) {
26548                         this.walk(e);
26549                     }
26550                     return;
26551                 
26552                 default:    // tags .. that do not match.
26553                     if (e.hasChildNodes()) {
26554                         this.walk(e);
26555                     }
26556             }
26557             
26558         }, this);
26559         
26560     },
26561     
26562     
26563     removeNodeKeepChildren : function( node)
26564     {
26565     
26566         ar = Array.from(node.childNodes);
26567         for (var i = 0; i < ar.length; i++) {
26568          
26569             node.removeChild(ar[i]);
26570             // what if we need to walk these???
26571             node.parentNode.insertBefore(ar[i], node);
26572            
26573         }
26574         node.parentNode.removeChild(node);
26575     }
26576 }; 
26577
26578 /**
26579  * @class Roo.htmleditor.FilterAttributes
26580  * clean attributes and  styles including http:// etc.. in attribute
26581  * @constructor
26582 * Run a new Attribute Filter
26583 * @param {Object} config Configuration options
26584  */
26585 Roo.htmleditor.FilterAttributes = function(cfg)
26586 {
26587     Roo.apply(this, cfg);
26588     this.attrib_black = this.attrib_black || [];
26589     this.attrib_white = this.attrib_white || [];
26590
26591     this.attrib_clean = this.attrib_clean || [];
26592     this.style_white = this.style_white || [];
26593     this.style_black = this.style_black || [];
26594     this.walk(cfg.node);
26595 }
26596
26597 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26598 {
26599     tag: true, // all tags
26600     
26601     attrib_black : false, // array
26602     attrib_clean : false,
26603     attrib_white : false,
26604
26605     style_white : false,
26606     style_black : false,
26607      
26608      
26609     replaceTag : function(node)
26610     {
26611         if (!node.attributes || !node.attributes.length) {
26612             return true;
26613         }
26614         
26615         for (var i = node.attributes.length-1; i > -1 ; i--) {
26616             var a = node.attributes[i];
26617             //console.log(a);
26618             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26619                 node.removeAttribute(a.name);
26620                 continue;
26621             }
26622             
26623             
26624             
26625             if (a.name.toLowerCase().substr(0,2)=='on')  {
26626                 node.removeAttribute(a.name);
26627                 continue;
26628             }
26629             
26630             
26631             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26632                 node.removeAttribute(a.name);
26633                 continue;
26634             }
26635             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26636                 this.cleanAttr(node,a.name,a.value); // fixme..
26637                 continue;
26638             }
26639             if (a.name == 'style') {
26640                 this.cleanStyle(node,a.name,a.value);
26641                 continue;
26642             }
26643             /// clean up MS crap..
26644             // tecnically this should be a list of valid class'es..
26645             
26646             
26647             if (a.name == 'class') {
26648                 if (a.value.match(/^Mso/)) {
26649                     node.removeAttribute('class');
26650                 }
26651                 
26652                 if (a.value.match(/^body$/)) {
26653                     node.removeAttribute('class');
26654                 }
26655                 continue;
26656             }
26657             
26658             
26659             // style cleanup!?
26660             // class cleanup?
26661             
26662         }
26663         return true; // clean children
26664     },
26665         
26666     cleanAttr: function(node, n,v)
26667     {
26668         
26669         if (v.match(/^\./) || v.match(/^\//)) {
26670             return;
26671         }
26672         if (v.match(/^(http|https):\/\//)
26673             || v.match(/^mailto:/) 
26674             || v.match(/^ftp:/)
26675             || v.match(/^data:/)
26676             ) {
26677             return;
26678         }
26679         if (v.match(/^#/)) {
26680             return;
26681         }
26682         if (v.match(/^\{/)) { // allow template editing.
26683             return;
26684         }
26685 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26686         node.removeAttribute(n);
26687         
26688     },
26689     cleanStyle : function(node,  n,v)
26690     {
26691         if (v.match(/expression/)) { //XSS?? should we even bother..
26692             node.removeAttribute(n);
26693             return;
26694         }
26695         
26696         var parts = v.split(/;/);
26697         var clean = [];
26698         
26699         Roo.each(parts, function(p) {
26700             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26701             if (!p.length) {
26702                 return true;
26703             }
26704             var l = p.split(':').shift().replace(/\s+/g,'');
26705             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26706             
26707             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26708                 return true;
26709             }
26710             //Roo.log()
26711             // only allow 'c whitelisted system attributes'
26712             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26713                 return true;
26714             }
26715             
26716             
26717             clean.push(p);
26718             return true;
26719         },this);
26720         if (clean.length) { 
26721             node.setAttribute(n, clean.join(';'));
26722         } else {
26723             node.removeAttribute(n);
26724         }
26725         
26726     }
26727         
26728         
26729         
26730     
26731 });/**
26732  * @class Roo.htmleditor.FilterBlack
26733  * remove blacklisted elements.
26734  * @constructor
26735  * Run a new Blacklisted Filter
26736  * @param {Object} config Configuration options
26737  */
26738
26739 Roo.htmleditor.FilterBlack = function(cfg)
26740 {
26741     Roo.apply(this, cfg);
26742     this.walk(cfg.node);
26743 }
26744
26745 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26746 {
26747     tag : true, // all elements.
26748    
26749     replaceTag : function(n)
26750     {
26751         n.parentNode.removeChild(n);
26752     }
26753 });
26754 /**
26755  * @class Roo.htmleditor.FilterComment
26756  * remove comments.
26757  * @constructor
26758 * Run a new Comments Filter
26759 * @param {Object} config Configuration options
26760  */
26761 Roo.htmleditor.FilterComment = function(cfg)
26762 {
26763     this.walk(cfg.node);
26764 }
26765
26766 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26767 {
26768   
26769     replaceComment : function(n)
26770     {
26771         n.parentNode.removeChild(n);
26772     }
26773 });/**
26774  * @class Roo.htmleditor.FilterKeepChildren
26775  * remove tags but keep children
26776  * @constructor
26777  * Run a new Keep Children Filter
26778  * @param {Object} config Configuration options
26779  */
26780
26781 Roo.htmleditor.FilterKeepChildren = function(cfg)
26782 {
26783     Roo.apply(this, cfg);
26784     if (this.tag === false) {
26785         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26786     }
26787     // hacky?
26788     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26789         this.cleanNamespace = true;
26790     }
26791         
26792     this.walk(cfg.node);
26793 }
26794
26795 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26796 {
26797     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26798   
26799     replaceTag : function(node)
26800     {
26801         // walk children...
26802         //Roo.log(node.tagName);
26803         var ar = Array.from(node.childNodes);
26804         //remove first..
26805         
26806         for (var i = 0; i < ar.length; i++) {
26807             var e = ar[i];
26808             if (e.nodeType == 1) {
26809                 if (
26810                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26811                     || // array and it matches
26812                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26813                     ||
26814                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26815                     ||
26816                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26817                 ) {
26818                     this.replaceTag(ar[i]); // child is blacklisted as well...
26819                     continue;
26820                 }
26821             }
26822         }  
26823         ar = Array.from(node.childNodes);
26824         for (var i = 0; i < ar.length; i++) {
26825          
26826             node.removeChild(ar[i]);
26827             // what if we need to walk these???
26828             node.parentNode.insertBefore(ar[i], node);
26829             if (this.tag !== false) {
26830                 this.walk(ar[i]);
26831                 
26832             }
26833         }
26834         //Roo.log("REMOVE:" + node.tagName);
26835         node.parentNode.removeChild(node);
26836         return false; // don't walk children
26837         
26838         
26839     }
26840 });/**
26841  * @class Roo.htmleditor.FilterParagraph
26842  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26843  * like on 'push' to remove the <p> tags and replace them with line breaks.
26844  * @constructor
26845  * Run a new Paragraph Filter
26846  * @param {Object} config Configuration options
26847  */
26848
26849 Roo.htmleditor.FilterParagraph = function(cfg)
26850 {
26851     // no need to apply config.
26852     this.walk(cfg.node);
26853 }
26854
26855 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26856 {
26857     
26858      
26859     tag : 'P',
26860     
26861      
26862     replaceTag : function(node)
26863     {
26864         
26865         if (node.childNodes.length == 1 &&
26866             node.childNodes[0].nodeType == 3 &&
26867             node.childNodes[0].textContent.trim().length < 1
26868             ) {
26869             // remove and replace with '<BR>';
26870             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26871             return false; // no need to walk..
26872         }
26873         var ar = Array.from(node.childNodes);
26874         for (var i = 0; i < ar.length; i++) {
26875             node.removeChild(ar[i]);
26876             // what if we need to walk these???
26877             node.parentNode.insertBefore(ar[i], node);
26878         }
26879         // now what about this?
26880         // <p> &nbsp; </p>
26881         
26882         // double BR.
26883         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26884         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26885         node.parentNode.removeChild(node);
26886         
26887         return false;
26888
26889     }
26890     
26891 });/**
26892  * @class Roo.htmleditor.FilterSpan
26893  * filter span's with no attributes out..
26894  * @constructor
26895  * Run a new Span Filter
26896  * @param {Object} config Configuration options
26897  */
26898
26899 Roo.htmleditor.FilterSpan = function(cfg)
26900 {
26901     // no need to apply config.
26902     this.walk(cfg.node);
26903 }
26904
26905 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26906 {
26907      
26908     tag : 'SPAN',
26909      
26910  
26911     replaceTag : function(node)
26912     {
26913         if (node.attributes && node.attributes.length > 0) {
26914             return true; // walk if there are any.
26915         }
26916         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26917         return false;
26918      
26919     }
26920     
26921 });/**
26922  * @class Roo.htmleditor.FilterTableWidth
26923   try and remove table width data - as that frequently messes up other stuff.
26924  * 
26925  *      was cleanTableWidths.
26926  *
26927  * Quite often pasting from word etc.. results in tables with column and widths.
26928  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26929  *
26930  * @constructor
26931  * Run a new Table Filter
26932  * @param {Object} config Configuration options
26933  */
26934
26935 Roo.htmleditor.FilterTableWidth = function(cfg)
26936 {
26937     // no need to apply config.
26938     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26939     this.walk(cfg.node);
26940 }
26941
26942 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26943 {
26944      
26945      
26946     
26947     replaceTag: function(node) {
26948         
26949         
26950       
26951         if (node.hasAttribute('width')) {
26952             node.removeAttribute('width');
26953         }
26954         
26955          
26956         if (node.hasAttribute("style")) {
26957             // pretty basic...
26958             
26959             var styles = node.getAttribute("style").split(";");
26960             var nstyle = [];
26961             Roo.each(styles, function(s) {
26962                 if (!s.match(/:/)) {
26963                     return;
26964                 }
26965                 var kv = s.split(":");
26966                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26967                     return;
26968                 }
26969                 // what ever is left... we allow.
26970                 nstyle.push(s);
26971             });
26972             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26973             if (!nstyle.length) {
26974                 node.removeAttribute('style');
26975             }
26976         }
26977         
26978         return true; // continue doing children..
26979     }
26980 });/**
26981  * @class Roo.htmleditor.FilterWord
26982  * try and clean up all the mess that Word generates.
26983  * 
26984  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26985  
26986  * @constructor
26987  * Run a new Span Filter
26988  * @param {Object} config Configuration options
26989  */
26990
26991 Roo.htmleditor.FilterWord = function(cfg)
26992 {
26993     // no need to apply config.
26994     this.replaceDocBullets(cfg.node);
26995     
26996     this.replaceAname(cfg.node);
26997     // this is disabled as the removal is done by other filters;
26998    // this.walk(cfg.node);
26999     this.replaceImageTable(cfg.node);
27000     
27001 }
27002
27003 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27004 {
27005     tag: true,
27006      
27007     
27008     /**
27009      * Clean up MS wordisms...
27010      */
27011     replaceTag : function(node)
27012     {
27013          
27014         // no idea what this does - span with text, replaceds with just text.
27015         if(
27016                 node.nodeName == 'SPAN' &&
27017                 !node.hasAttributes() &&
27018                 node.childNodes.length == 1 &&
27019                 node.firstChild.nodeName == "#text"  
27020         ) {
27021             var textNode = node.firstChild;
27022             node.removeChild(textNode);
27023             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27024                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27025             }
27026             node.parentNode.insertBefore(textNode, node);
27027             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27028                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27029             }
27030             
27031             node.parentNode.removeChild(node);
27032             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27033         }
27034         
27035    
27036         
27037         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27038             node.parentNode.removeChild(node);
27039             return false; // dont do chidlren
27040         }
27041         //Roo.log(node.tagName);
27042         // remove - but keep children..
27043         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27044             //Roo.log('-- removed');
27045             while (node.childNodes.length) {
27046                 var cn = node.childNodes[0];
27047                 node.removeChild(cn);
27048                 node.parentNode.insertBefore(cn, node);
27049                 // move node to parent - and clean it..
27050                 if (cn.nodeType == 1) {
27051                     this.replaceTag(cn);
27052                 }
27053                 
27054             }
27055             node.parentNode.removeChild(node);
27056             /// no need to iterate chidlren = it's got none..
27057             //this.iterateChildren(node, this.cleanWord);
27058             return false; // no need to iterate children.
27059         }
27060         // clean styles
27061         if (node.className.length) {
27062             
27063             var cn = node.className.split(/\W+/);
27064             var cna = [];
27065             Roo.each(cn, function(cls) {
27066                 if (cls.match(/Mso[a-zA-Z]+/)) {
27067                     return;
27068                 }
27069                 cna.push(cls);
27070             });
27071             node.className = cna.length ? cna.join(' ') : '';
27072             if (!cna.length) {
27073                 node.removeAttribute("class");
27074             }
27075         }
27076         
27077         if (node.hasAttribute("lang")) {
27078             node.removeAttribute("lang");
27079         }
27080         
27081         if (node.hasAttribute("style")) {
27082             
27083             var styles = node.getAttribute("style").split(";");
27084             var nstyle = [];
27085             Roo.each(styles, function(s) {
27086                 if (!s.match(/:/)) {
27087                     return;
27088                 }
27089                 var kv = s.split(":");
27090                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27091                     return;
27092                 }
27093                 // what ever is left... we allow.
27094                 nstyle.push(s);
27095             });
27096             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27097             if (!nstyle.length) {
27098                 node.removeAttribute('style');
27099             }
27100         }
27101         return true; // do children
27102         
27103         
27104         
27105     },
27106     
27107     styleToObject: function(node)
27108     {
27109         var styles = (node.getAttribute("style") || '').split(";");
27110         var ret = {};
27111         Roo.each(styles, function(s) {
27112             if (!s.match(/:/)) {
27113                 return;
27114             }
27115             var kv = s.split(":");
27116              
27117             // what ever is left... we allow.
27118             ret[kv[0].trim()] = kv[1];
27119         });
27120         return ret;
27121     },
27122     
27123     
27124     replaceAname : function (doc)
27125     {
27126         // replace all the a/name without..
27127         var aa = Array.from(doc.getElementsByTagName('a'));
27128         for (var i = 0; i  < aa.length; i++) {
27129             var a = aa[i];
27130             if (a.hasAttribute("name")) {
27131                 a.removeAttribute("name");
27132             }
27133             if (a.hasAttribute("href")) {
27134                 continue;
27135             }
27136             // reparent children.
27137             this.removeNodeKeepChildren(a);
27138             
27139         }
27140         
27141         
27142         
27143     },
27144
27145     
27146     
27147     replaceDocBullets : function(doc)
27148     {
27149         // this is a bit odd - but it appears some indents use ql-indent-1
27150          //Roo.log(doc.innerHTML);
27151         
27152         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27153         for( var i = 0; i < listpara.length; i ++) {
27154             listpara[i].className = "MsoListParagraph";
27155         }
27156         
27157         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27158         for( var i = 0; i < listpara.length; i ++) {
27159             listpara[i].className = "MsoListParagraph";
27160         }
27161         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27162         for( var i = 0; i < listpara.length; i ++) {
27163             listpara[i].className = "MsoListParagraph";
27164         }
27165         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
27166         for( var i = 0; i < listpara.length; i ++) {
27167             listpara[i].className = "MsoListParagraph";
27168         }
27169         
27170         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27171         var htwo =  Array.from(doc.getElementsByTagName('h2'));
27172         for( var i = 0; i < htwo.length; i ++) {
27173             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27174                 htwo[i].className = "MsoListParagraph";
27175             }
27176         }
27177         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
27178         for( var i = 0; i < listpara.length; i ++) {
27179             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27180                 listpara[i].className = "MsoListParagraph";
27181             } else {
27182                 listpara[i].className = "MsoNormalx";
27183             }
27184         }
27185        
27186         listpara = doc.getElementsByClassName('MsoListParagraph');
27187         // Roo.log(doc.innerHTML);
27188         
27189         
27190         
27191         while(listpara.length) {
27192             
27193             this.replaceDocBullet(listpara.item(0));
27194         }
27195       
27196     },
27197     
27198      
27199     
27200     replaceDocBullet : function(p)
27201     {
27202         // gather all the siblings.
27203         var ns = p,
27204             parent = p.parentNode,
27205             doc = parent.ownerDocument,
27206             items = [];
27207          
27208         //Roo.log("Parsing: " + p.innerText)    ;
27209         var listtype = 'ul';   
27210         while (ns) {
27211             if (ns.nodeType != 1) {
27212                 ns = ns.nextSibling;
27213                 continue;
27214             }
27215             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27216                 //Roo.log("Missing para r q1indent - got:" + ns.className);
27217                 break;
27218             }
27219             var spans = ns.getElementsByTagName('span');
27220             
27221             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27222                 items.push(ns);
27223                 ns = ns.nextSibling;
27224                 has_list = true;
27225                 if (!spans.length) {
27226                     continue;
27227                 }
27228                 var ff = '';
27229                 var se = spans[0];
27230                 for (var i = 0; i < spans.length;i++) {
27231                     se = spans[i];
27232                     if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
27233                         ff = se.style.fontFamily;
27234                         break;
27235                     }
27236                 }
27237                  
27238                     
27239                 //Roo.log("got font family: " + ff);
27240                 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
27241                     listtype = 'ol';
27242                 }
27243                 
27244                 continue;
27245             }
27246             //Roo.log("no mso-list?");
27247             
27248             var spans = ns.getElementsByTagName('span');
27249             if (!spans.length) {
27250                 break;
27251             }
27252             var has_list  = false;
27253             for(var i = 0; i < spans.length; i++) {
27254                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27255                     has_list = true;
27256                     break;
27257                 }
27258             }
27259             if (!has_list) {
27260                 break;
27261             }
27262             items.push(ns);
27263             ns = ns.nextSibling;
27264             
27265             
27266         }
27267         if (!items.length) {
27268             ns.className = "";
27269             return;
27270         }
27271         
27272         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27273         parent.insertBefore(ul, p);
27274         var lvl = 0;
27275         var stack = [ ul ];
27276         var last_li = false;
27277         
27278         var margin_to_depth = {};
27279         max_margins = -1;
27280         
27281         items.forEach(function(n, ipos) {
27282             //Roo.log("got innertHMLT=" + n.innerHTML);
27283             
27284             var spans = n.getElementsByTagName('span');
27285             if (!spans.length) {
27286                 //Roo.log("No spans found");
27287                  
27288                 parent.removeChild(n);
27289                 
27290                 
27291                 return; // skip it...
27292             }
27293            
27294                 
27295             var num = 1;
27296             var style = {};
27297             for(var i = 0; i < spans.length; i++) {
27298             
27299                 style = this.styleToObject(spans[i]);
27300                 if (typeof(style['mso-list']) == 'undefined') {
27301                     continue;
27302                 }
27303                 if (listtype == 'ol') {
27304                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
27305                 }
27306                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27307                 break;
27308             }
27309             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27310             style = this.styleToObject(n); // mo-list is from the parent node.
27311             if (typeof(style['mso-list']) == 'undefined') {
27312                 //Roo.log("parent is missing level");
27313                   
27314                 parent.removeChild(n);
27315                  
27316                 return;
27317             }
27318             
27319             var margin = style['margin-left'];
27320             if (typeof(margin_to_depth[margin]) == 'undefined') {
27321                 max_margins++;
27322                 margin_to_depth[margin] = max_margins;
27323             }
27324             nlvl = margin_to_depth[margin] ;
27325              
27326             if (nlvl > lvl) {
27327                 //new indent
27328                 var nul = doc.createElement(listtype); // what about number lists...
27329                 if (!last_li) {
27330                     last_li = doc.createElement('li');
27331                     stack[lvl].appendChild(last_li);
27332                 }
27333                 last_li.appendChild(nul);
27334                 stack[nlvl] = nul;
27335                 
27336             }
27337             lvl = nlvl;
27338             
27339             // not starting at 1..
27340             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27341                 stack[nlvl].setAttribute("start", num);
27342             }
27343             
27344             var nli = stack[nlvl].appendChild(doc.createElement('li'));
27345             last_li = nli;
27346             nli.innerHTML = n.innerHTML;
27347             //Roo.log("innerHTML = " + n.innerHTML);
27348             parent.removeChild(n);
27349             
27350              
27351              
27352             
27353         },this);
27354         
27355         
27356         
27357         
27358     },
27359     
27360     replaceImageTable : function(doc)
27361     {
27362          /*
27363           <table cellpadding=0 cellspacing=0 align=left>
27364   <tr>
27365    <td width=423 height=0></td>
27366   </tr>
27367   <tr>
27368    <td></td>
27369    <td><img width=601 height=401
27370    src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
27371    v:shapes="Picture_x0020_2"></td>
27372   </tr>
27373  </table>
27374  */
27375         var imgs = Array.from(doc.getElementsByTagName('img'));
27376         Roo.each(imgs, function(img) {
27377             var td = img.parentNode;
27378             if (td.nodeName !=  'TD') {
27379                 return;
27380             }
27381             var tr = td.parentNode;
27382             if (tr.nodeName !=  'TR') {
27383                 return;
27384             }
27385             var tbody = tr.parentNode;
27386             if (tbody.nodeName !=  'TBODY') {
27387                 return;
27388             }
27389             var table = tbody.parentNode;
27390             if (table.nodeName !=  'TABLE') {
27391                 return;
27392             }
27393             // first row..
27394             
27395             if (table.getElementsByTagName('tr').length != 2) {
27396                 return;
27397             }
27398             if (table.getElementsByTagName('td').length != 3) {
27399                 return;
27400             }
27401             if (table.innerText.trim() != '') {
27402                 return;
27403             }
27404             var p = table.parentNode;
27405             img.parentNode.removeChild(img);
27406             p.insertBefore(img, table);
27407             p.removeChild(table);
27408             
27409             
27410             
27411         });
27412         
27413       
27414     }
27415     
27416 });
27417 /**
27418  * @class Roo.htmleditor.FilterStyleToTag
27419  * part of the word stuff... - certain 'styles' should be converted to tags.
27420  * eg.
27421  *   font-weight: bold -> bold
27422  *   ?? super / subscrit etc..
27423  * 
27424  * @constructor
27425 * Run a new style to tag filter.
27426 * @param {Object} config Configuration options
27427  */
27428 Roo.htmleditor.FilterStyleToTag = function(cfg)
27429 {
27430     
27431     this.tags = {
27432         B  : [ 'fontWeight' , 'bold'],
27433         I :  [ 'fontStyle' , 'italic'],
27434         //pre :  [ 'font-style' , 'italic'],
27435         // h1.. h6 ?? font-size?
27436         SUP : [ 'verticalAlign' , 'super' ],
27437         SUB : [ 'verticalAlign' , 'sub' ]
27438         
27439         
27440     };
27441     
27442     Roo.apply(this, cfg);
27443      
27444     
27445     this.walk(cfg.node);
27446     
27447     
27448     
27449 }
27450
27451
27452 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27453 {
27454     tag: true, // all tags
27455     
27456     tags : false,
27457     
27458     
27459     replaceTag : function(node)
27460     {
27461         
27462         
27463         if (node.getAttribute("style") === null) {
27464             return true;
27465         }
27466         var inject = [];
27467         for (var k in this.tags) {
27468             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27469                 inject.push(k);
27470                 node.style.removeProperty(this.tags[k][0]);
27471             }
27472         }
27473         if (!inject.length) {
27474             return true; 
27475         }
27476         var cn = Array.from(node.childNodes);
27477         var nn = node;
27478         Roo.each(inject, function(t) {
27479             var nc = node.ownerDocument.createElement(t);
27480             nn.appendChild(nc);
27481             nn = nc;
27482         });
27483         for(var i = 0;i < cn.length;cn++) {
27484             node.removeChild(cn[i]);
27485             nn.appendChild(cn[i]);
27486         }
27487         return true /// iterate thru
27488     }
27489     
27490 })/**
27491  * @class Roo.htmleditor.FilterLongBr
27492  * BR/BR/BR - keep a maximum of 2...
27493  * @constructor
27494  * Run a new Long BR Filter
27495  * @param {Object} config Configuration options
27496  */
27497
27498 Roo.htmleditor.FilterLongBr = function(cfg)
27499 {
27500     // no need to apply config.
27501     this.walk(cfg.node);
27502 }
27503
27504 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27505 {
27506     
27507      
27508     tag : 'BR',
27509     
27510      
27511     replaceTag : function(node)
27512     {
27513         
27514         var ps = node.nextSibling;
27515         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27516             ps = ps.nextSibling;
27517         }
27518         
27519         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
27520             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27521             return false;
27522         }
27523         
27524         if (!ps || ps.nodeType != 1) {
27525             return false;
27526         }
27527         
27528         if (!ps || ps.tagName != 'BR') {
27529            
27530             return false;
27531         }
27532         
27533         
27534         
27535         
27536         
27537         if (!node.previousSibling) {
27538             return false;
27539         }
27540         var ps = node.previousSibling;
27541         
27542         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27543             ps = ps.previousSibling;
27544         }
27545         if (!ps || ps.nodeType != 1) {
27546             return false;
27547         }
27548         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27549         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27550             return false;
27551         }
27552         
27553         node.parentNode.removeChild(node); // remove me...
27554         
27555         return false; // no need to do children
27556
27557     }
27558     
27559 }); 
27560
27561 /**
27562  * @class Roo.htmleditor.FilterBlock
27563  * removes id / data-block and contenteditable that are associated with blocks
27564  * usage should be done on a cloned copy of the dom
27565  * @constructor
27566 * Run a new Attribute Filter { node : xxxx }}
27567 * @param {Object} config Configuration options
27568  */
27569 Roo.htmleditor.FilterBlock = function(cfg)
27570 {
27571     Roo.apply(this, cfg);
27572     var qa = cfg.node.querySelectorAll;
27573     this.removeAttributes('data-block');
27574     this.removeAttributes('contenteditable');
27575     this.removeAttributes('id');
27576     
27577 }
27578
27579 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27580 {
27581     node: true, // all tags
27582      
27583      
27584     removeAttributes : function(attr)
27585     {
27586         var ar = this.node.querySelectorAll('*[' + attr + ']');
27587         for (var i =0;i<ar.length;i++) {
27588             ar[i].removeAttribute(attr);
27589         }
27590     }
27591         
27592         
27593         
27594     
27595 });
27596 /***
27597  * This is based loosely on tinymce 
27598  * @class Roo.htmleditor.TidySerializer
27599  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27600  * @constructor
27601  * @method Serializer
27602  * @param {Object} settings Name/value settings object.
27603  */
27604
27605
27606 Roo.htmleditor.TidySerializer = function(settings)
27607 {
27608     Roo.apply(this, settings);
27609     
27610     this.writer = new Roo.htmleditor.TidyWriter(settings);
27611     
27612     
27613
27614 };
27615 Roo.htmleditor.TidySerializer.prototype = {
27616     
27617     /**
27618      * @param {boolean} inner do the inner of the node.
27619      */
27620     inner : false,
27621     
27622     writer : false,
27623     
27624     /**
27625     * Serializes the specified node into a string.
27626     *
27627     * @example
27628     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27629     * @method serialize
27630     * @param {DomElement} node Node instance to serialize.
27631     * @return {String} String with HTML based on DOM tree.
27632     */
27633     serialize : function(node) {
27634         
27635         // = settings.validate;
27636         var writer = this.writer;
27637         var self  = this;
27638         this.handlers = {
27639             // #text
27640             3: function(node) {
27641                 
27642                 writer.text(node.nodeValue, node);
27643             },
27644             // #comment
27645             8: function(node) {
27646                 writer.comment(node.nodeValue);
27647             },
27648             // Processing instruction
27649             7: function(node) {
27650                 writer.pi(node.name, node.nodeValue);
27651             },
27652             // Doctype
27653             10: function(node) {
27654                 writer.doctype(node.nodeValue);
27655             },
27656             // CDATA
27657             4: function(node) {
27658                 writer.cdata(node.nodeValue);
27659             },
27660             // Document fragment
27661             11: function(node) {
27662                 node = node.firstChild;
27663                 if (!node) {
27664                     return;
27665                 }
27666                 while(node) {
27667                     self.walk(node);
27668                     node = node.nextSibling
27669                 }
27670             }
27671         };
27672         writer.reset();
27673         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27674         return writer.getContent();
27675     },
27676
27677     walk: function(node)
27678     {
27679         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27680             handler = this.handlers[node.nodeType];
27681             
27682         if (handler) {
27683             handler(node);
27684             return;
27685         }
27686     
27687         var name = node.nodeName;
27688         var isEmpty = node.childNodes.length < 1;
27689       
27690         var writer = this.writer;
27691         var attrs = node.attributes;
27692         // Sort attributes
27693         
27694         writer.start(node.nodeName, attrs, isEmpty, node);
27695         if (isEmpty) {
27696             return;
27697         }
27698         node = node.firstChild;
27699         if (!node) {
27700             writer.end(name);
27701             return;
27702         }
27703         while (node) {
27704             this.walk(node);
27705             node = node.nextSibling;
27706         }
27707         writer.end(name);
27708         
27709     
27710     }
27711     // Serialize element and treat all non elements as fragments
27712    
27713 }; 
27714
27715 /***
27716  * This is based loosely on tinymce 
27717  * @class Roo.htmleditor.TidyWriter
27718  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27719  *
27720  * Known issues?
27721  * - not tested much with 'PRE' formated elements.
27722  * 
27723  *
27724  *
27725  */
27726
27727 Roo.htmleditor.TidyWriter = function(settings)
27728 {
27729     
27730     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27731     Roo.apply(this, settings);
27732     this.html = [];
27733     this.state = [];
27734      
27735     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27736   
27737 }
27738 Roo.htmleditor.TidyWriter.prototype = {
27739
27740  
27741     state : false,
27742     
27743     indent :  '  ',
27744     
27745     // part of state...
27746     indentstr : '',
27747     in_pre: false,
27748     in_inline : false,
27749     last_inline : false,
27750     encode : false,
27751      
27752     
27753             /**
27754     * Writes the a start element such as <p id="a">.
27755     *
27756     * @method start
27757     * @param {String} name Name of the element.
27758     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27759     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27760     */
27761     start: function(name, attrs, empty, node)
27762     {
27763         var i, l, attr, value;
27764         
27765         // there are some situations where adding line break && indentation will not work. will not work.
27766         // <span / b / i ... formating?
27767         
27768         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27769         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27770         
27771         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27772         
27773         var add_lb = name == 'BR' ? false : in_inline;
27774         
27775         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27776             i_inline = false;
27777         }
27778
27779         var indentstr =  this.indentstr;
27780         
27781         // e_inline = elements that can be inline, but still allow \n before and after?
27782         // only 'BR' ??? any others?
27783         
27784         // ADD LINE BEFORE tage
27785         if (!this.in_pre) {
27786             if (in_inline) {
27787                 //code
27788                 if (name == 'BR') {
27789                     this.addLine();
27790                 } else if (this.lastElementEndsWS()) {
27791                     this.addLine();
27792                 } else{
27793                     // otherwise - no new line. (and dont indent.)
27794                     indentstr = '';
27795                 }
27796                 
27797             } else {
27798                 this.addLine();
27799             }
27800         } else {
27801             indentstr = '';
27802         }
27803         
27804         this.html.push(indentstr + '<', name.toLowerCase());
27805         
27806         if (attrs) {
27807             for (i = 0, l = attrs.length; i < l; i++) {
27808                 attr = attrs[i];
27809                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27810             }
27811         }
27812      
27813         if (empty) {
27814             if (is_short) {
27815                 this.html[this.html.length] = '/>';
27816             } else {
27817                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27818             }
27819             var e_inline = name == 'BR' ? false : this.in_inline;
27820             
27821             if (!e_inline && !this.in_pre) {
27822                 this.addLine();
27823             }
27824             return;
27825         
27826         }
27827         // not empty..
27828         this.html[this.html.length] = '>';
27829         
27830         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27831         /*
27832         if (!in_inline && !in_pre) {
27833             var cn = node.firstChild;
27834             while(cn) {
27835                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27836                     in_inline = true
27837                     break;
27838                 }
27839                 cn = cn.nextSibling;
27840             }
27841              
27842         }
27843         */
27844         
27845         
27846         this.pushState({
27847             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27848             in_pre : in_pre,
27849             in_inline :  in_inline
27850         });
27851         // add a line after if we are not in a
27852         
27853         if (!in_inline && !in_pre) {
27854             this.addLine();
27855         }
27856         
27857             
27858          
27859         
27860     },
27861     
27862     lastElementEndsWS : function()
27863     {
27864         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27865         if (value === false) {
27866             return true;
27867         }
27868         return value.match(/\s+$/);
27869         
27870     },
27871     
27872     /**
27873      * Writes the a end element such as </p>.
27874      *
27875      * @method end
27876      * @param {String} name Name of the element.
27877      */
27878     end: function(name) {
27879         var value;
27880         this.popState();
27881         var indentstr = '';
27882         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27883         
27884         if (!this.in_pre && !in_inline) {
27885             this.addLine();
27886             indentstr  = this.indentstr;
27887         }
27888         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27889         this.last_inline = in_inline;
27890         
27891         // pop the indent state..
27892     },
27893     /**
27894      * Writes a text node.
27895      *
27896      * In pre - we should not mess with the contents.
27897      * 
27898      *
27899      * @method text
27900      * @param {String} text String to write out.
27901      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27902      */
27903     text: function(in_text, node)
27904     {
27905         // if not in whitespace critical
27906         if (in_text.length < 1) {
27907             return;
27908         }
27909         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27910         
27911         if (this.in_pre) {
27912             this.html[this.html.length] =  text;
27913             return;   
27914         }
27915         
27916         if (this.in_inline) {
27917             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27918             if (text != ' ') {
27919                 text = text.replace(/\s+/,' ');  // all white space to single white space
27920                 
27921                     
27922                 // if next tag is '<BR>', then we can trim right..
27923                 if (node.nextSibling &&
27924                     node.nextSibling.nodeType == 1 &&
27925                     node.nextSibling.nodeName == 'BR' )
27926                 {
27927                     text = text.replace(/\s+$/g,'');
27928                 }
27929                 // if previous tag was a BR, we can also trim..
27930                 if (node.previousSibling &&
27931                     node.previousSibling.nodeType == 1 &&
27932                     node.previousSibling.nodeName == 'BR' )
27933                 {
27934                     text = this.indentstr +  text.replace(/^\s+/g,'');
27935                 }
27936                 if (text.match(/\n/)) {
27937                     text = text.replace(
27938                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27939                     );
27940                     // remoeve the last whitespace / line break.
27941                     text = text.replace(/\n\s+$/,'');
27942                 }
27943                 // repace long lines
27944                 
27945             }
27946              
27947             this.html[this.html.length] =  text;
27948             return;   
27949         }
27950         // see if previous element was a inline element.
27951         var indentstr = this.indentstr;
27952    
27953         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27954         
27955         // should trim left?
27956         if (node.previousSibling &&
27957             node.previousSibling.nodeType == 1 &&
27958             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27959         {
27960             indentstr = '';
27961             
27962         } else {
27963             this.addLine();
27964             text = text.replace(/^\s+/,''); // trim left
27965           
27966         }
27967         // should trim right?
27968         if (node.nextSibling &&
27969             node.nextSibling.nodeType == 1 &&
27970             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27971         {
27972           // noop
27973             
27974         }  else {
27975             text = text.replace(/\s+$/,''); // trim right
27976         }
27977          
27978               
27979         
27980         
27981         
27982         if (text.length < 1) {
27983             return;
27984         }
27985         if (!text.match(/\n/)) {
27986             this.html.push(indentstr + text);
27987             return;
27988         }
27989         
27990         text = this.indentstr + text.replace(
27991             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27992         );
27993         // remoeve the last whitespace / line break.
27994         text = text.replace(/\s+$/,''); 
27995         
27996         this.html.push(text);
27997         
27998         // split and indent..
27999         
28000         
28001     },
28002     /**
28003      * Writes a cdata node such as <![CDATA[data]]>.
28004      *
28005      * @method cdata
28006      * @param {String} text String to write out inside the cdata.
28007      */
28008     cdata: function(text) {
28009         this.html.push('<![CDATA[', text, ']]>');
28010     },
28011     /**
28012     * Writes a comment node such as <!-- Comment -->.
28013     *
28014     * @method cdata
28015     * @param {String} text String to write out inside the comment.
28016     */
28017    comment: function(text) {
28018        this.html.push('<!--', text, '-->');
28019    },
28020     /**
28021      * Writes a PI node such as <?xml attr="value" ?>.
28022      *
28023      * @method pi
28024      * @param {String} name Name of the pi.
28025      * @param {String} text String to write out inside the pi.
28026      */
28027     pi: function(name, text) {
28028         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
28029         this.indent != '' && this.html.push('\n');
28030     },
28031     /**
28032      * Writes a doctype node such as <!DOCTYPE data>.
28033      *
28034      * @method doctype
28035      * @param {String} text String to write out inside the doctype.
28036      */
28037     doctype: function(text) {
28038         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
28039     },
28040     /**
28041      * Resets the internal buffer if one wants to reuse the writer.
28042      *
28043      * @method reset
28044      */
28045     reset: function() {
28046         this.html.length = 0;
28047         this.state = [];
28048         this.pushState({
28049             indentstr : '',
28050             in_pre : false, 
28051             in_inline : false
28052         })
28053     },
28054     /**
28055      * Returns the contents that got serialized.
28056      *
28057      * @method getContent
28058      * @return {String} HTML contents that got written down.
28059      */
28060     getContent: function() {
28061         return this.html.join('').replace(/\n$/, '');
28062     },
28063     
28064     pushState : function(cfg)
28065     {
28066         this.state.push(cfg);
28067         Roo.apply(this, cfg);
28068     },
28069     
28070     popState : function()
28071     {
28072         if (this.state.length < 1) {
28073             return; // nothing to push
28074         }
28075         var cfg = {
28076             in_pre: false,
28077             indentstr : ''
28078         };
28079         this.state.pop();
28080         if (this.state.length > 0) {
28081             cfg = this.state[this.state.length-1]; 
28082         }
28083         Roo.apply(this, cfg);
28084     },
28085     
28086     addLine: function()
28087     {
28088         if (this.html.length < 1) {
28089             return;
28090         }
28091         
28092         
28093         var value = this.html[this.html.length - 1];
28094         if (value.length > 0 && '\n' !== value) {
28095             this.html.push('\n');
28096         }
28097     }
28098     
28099     
28100 //'pre script noscript style textarea video audio iframe object code'
28101 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
28102 // inline 
28103 };
28104
28105 Roo.htmleditor.TidyWriter.inline_elements = [
28106         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28107         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28108 ];
28109 Roo.htmleditor.TidyWriter.shortend_elements = [
28110     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28111     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28112 ];
28113
28114 Roo.htmleditor.TidyWriter.whitespace_elements = [
28115     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28116 ];/***
28117  * This is based loosely on tinymce 
28118  * @class Roo.htmleditor.TidyEntities
28119  * @static
28120  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28121  *
28122  * Not 100% sure this is actually used or needed.
28123  */
28124
28125 Roo.htmleditor.TidyEntities = {
28126     
28127     /**
28128      * initialize data..
28129      */
28130     init : function (){
28131      
28132         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28133        
28134     },
28135
28136
28137     buildEntitiesLookup: function(items, radix) {
28138         var i, chr, entity, lookup = {};
28139         if (!items) {
28140             return {};
28141         }
28142         items = typeof(items) == 'string' ? items.split(',') : items;
28143         radix = radix || 10;
28144         // Build entities lookup table
28145         for (i = 0; i < items.length; i += 2) {
28146             chr = String.fromCharCode(parseInt(items[i], radix));
28147             // Only add non base entities
28148             if (!this.baseEntities[chr]) {
28149                 entity = '&' + items[i + 1] + ';';
28150                 lookup[chr] = entity;
28151                 lookup[entity] = chr;
28152             }
28153         }
28154         return lookup;
28155         
28156     },
28157     
28158     asciiMap : {
28159             128: '€',
28160             130: '‚',
28161             131: 'ƒ',
28162             132: '„',
28163             133: '…',
28164             134: '†',
28165             135: '‡',
28166             136: 'ˆ',
28167             137: '‰',
28168             138: 'Š',
28169             139: '‹',
28170             140: 'Œ',
28171             142: 'Ž',
28172             145: '‘',
28173             146: '’',
28174             147: '“',
28175             148: '”',
28176             149: '•',
28177             150: '–',
28178             151: '—',
28179             152: '˜',
28180             153: '™',
28181             154: 'š',
28182             155: '›',
28183             156: 'œ',
28184             158: 'ž',
28185             159: 'Ÿ'
28186     },
28187     // Raw entities
28188     baseEntities : {
28189         '"': '&quot;',
28190         // Needs to be escaped since the YUI compressor would otherwise break the code
28191         '\'': '&#39;',
28192         '<': '&lt;',
28193         '>': '&gt;',
28194         '&': '&amp;',
28195         '`': '&#96;'
28196     },
28197     // Reverse lookup table for raw entities
28198     reverseEntities : {
28199         '&lt;': '<',
28200         '&gt;': '>',
28201         '&amp;': '&',
28202         '&quot;': '"',
28203         '&apos;': '\''
28204     },
28205     
28206     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28207     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28208     rawCharsRegExp : /[<>&\"\']/g,
28209     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28210     namedEntities  : false,
28211     namedEntitiesData : [ 
28212         '50',
28213         'nbsp',
28214         '51',
28215         'iexcl',
28216         '52',
28217         'cent',
28218         '53',
28219         'pound',
28220         '54',
28221         'curren',
28222         '55',
28223         'yen',
28224         '56',
28225         'brvbar',
28226         '57',
28227         'sect',
28228         '58',
28229         'uml',
28230         '59',
28231         'copy',
28232         '5a',
28233         'ordf',
28234         '5b',
28235         'laquo',
28236         '5c',
28237         'not',
28238         '5d',
28239         'shy',
28240         '5e',
28241         'reg',
28242         '5f',
28243         'macr',
28244         '5g',
28245         'deg',
28246         '5h',
28247         'plusmn',
28248         '5i',
28249         'sup2',
28250         '5j',
28251         'sup3',
28252         '5k',
28253         'acute',
28254         '5l',
28255         'micro',
28256         '5m',
28257         'para',
28258         '5n',
28259         'middot',
28260         '5o',
28261         'cedil',
28262         '5p',
28263         'sup1',
28264         '5q',
28265         'ordm',
28266         '5r',
28267         'raquo',
28268         '5s',
28269         'frac14',
28270         '5t',
28271         'frac12',
28272         '5u',
28273         'frac34',
28274         '5v',
28275         'iquest',
28276         '60',
28277         'Agrave',
28278         '61',
28279         'Aacute',
28280         '62',
28281         'Acirc',
28282         '63',
28283         'Atilde',
28284         '64',
28285         'Auml',
28286         '65',
28287         'Aring',
28288         '66',
28289         'AElig',
28290         '67',
28291         'Ccedil',
28292         '68',
28293         'Egrave',
28294         '69',
28295         'Eacute',
28296         '6a',
28297         'Ecirc',
28298         '6b',
28299         'Euml',
28300         '6c',
28301         'Igrave',
28302         '6d',
28303         'Iacute',
28304         '6e',
28305         'Icirc',
28306         '6f',
28307         'Iuml',
28308         '6g',
28309         'ETH',
28310         '6h',
28311         'Ntilde',
28312         '6i',
28313         'Ograve',
28314         '6j',
28315         'Oacute',
28316         '6k',
28317         'Ocirc',
28318         '6l',
28319         'Otilde',
28320         '6m',
28321         'Ouml',
28322         '6n',
28323         'times',
28324         '6o',
28325         'Oslash',
28326         '6p',
28327         'Ugrave',
28328         '6q',
28329         'Uacute',
28330         '6r',
28331         'Ucirc',
28332         '6s',
28333         'Uuml',
28334         '6t',
28335         'Yacute',
28336         '6u',
28337         'THORN',
28338         '6v',
28339         'szlig',
28340         '70',
28341         'agrave',
28342         '71',
28343         'aacute',
28344         '72',
28345         'acirc',
28346         '73',
28347         'atilde',
28348         '74',
28349         'auml',
28350         '75',
28351         'aring',
28352         '76',
28353         'aelig',
28354         '77',
28355         'ccedil',
28356         '78',
28357         'egrave',
28358         '79',
28359         'eacute',
28360         '7a',
28361         'ecirc',
28362         '7b',
28363         'euml',
28364         '7c',
28365         'igrave',
28366         '7d',
28367         'iacute',
28368         '7e',
28369         'icirc',
28370         '7f',
28371         'iuml',
28372         '7g',
28373         'eth',
28374         '7h',
28375         'ntilde',
28376         '7i',
28377         'ograve',
28378         '7j',
28379         'oacute',
28380         '7k',
28381         'ocirc',
28382         '7l',
28383         'otilde',
28384         '7m',
28385         'ouml',
28386         '7n',
28387         'divide',
28388         '7o',
28389         'oslash',
28390         '7p',
28391         'ugrave',
28392         '7q',
28393         'uacute',
28394         '7r',
28395         'ucirc',
28396         '7s',
28397         'uuml',
28398         '7t',
28399         'yacute',
28400         '7u',
28401         'thorn',
28402         '7v',
28403         'yuml',
28404         'ci',
28405         'fnof',
28406         'sh',
28407         'Alpha',
28408         'si',
28409         'Beta',
28410         'sj',
28411         'Gamma',
28412         'sk',
28413         'Delta',
28414         'sl',
28415         'Epsilon',
28416         'sm',
28417         'Zeta',
28418         'sn',
28419         'Eta',
28420         'so',
28421         'Theta',
28422         'sp',
28423         'Iota',
28424         'sq',
28425         'Kappa',
28426         'sr',
28427         'Lambda',
28428         'ss',
28429         'Mu',
28430         'st',
28431         'Nu',
28432         'su',
28433         'Xi',
28434         'sv',
28435         'Omicron',
28436         't0',
28437         'Pi',
28438         't1',
28439         'Rho',
28440         't3',
28441         'Sigma',
28442         't4',
28443         'Tau',
28444         't5',
28445         'Upsilon',
28446         't6',
28447         'Phi',
28448         't7',
28449         'Chi',
28450         't8',
28451         'Psi',
28452         't9',
28453         'Omega',
28454         'th',
28455         'alpha',
28456         'ti',
28457         'beta',
28458         'tj',
28459         'gamma',
28460         'tk',
28461         'delta',
28462         'tl',
28463         'epsilon',
28464         'tm',
28465         'zeta',
28466         'tn',
28467         'eta',
28468         'to',
28469         'theta',
28470         'tp',
28471         'iota',
28472         'tq',
28473         'kappa',
28474         'tr',
28475         'lambda',
28476         'ts',
28477         'mu',
28478         'tt',
28479         'nu',
28480         'tu',
28481         'xi',
28482         'tv',
28483         'omicron',
28484         'u0',
28485         'pi',
28486         'u1',
28487         'rho',
28488         'u2',
28489         'sigmaf',
28490         'u3',
28491         'sigma',
28492         'u4',
28493         'tau',
28494         'u5',
28495         'upsilon',
28496         'u6',
28497         'phi',
28498         'u7',
28499         'chi',
28500         'u8',
28501         'psi',
28502         'u9',
28503         'omega',
28504         'uh',
28505         'thetasym',
28506         'ui',
28507         'upsih',
28508         'um',
28509         'piv',
28510         '812',
28511         'bull',
28512         '816',
28513         'hellip',
28514         '81i',
28515         'prime',
28516         '81j',
28517         'Prime',
28518         '81u',
28519         'oline',
28520         '824',
28521         'frasl',
28522         '88o',
28523         'weierp',
28524         '88h',
28525         'image',
28526         '88s',
28527         'real',
28528         '892',
28529         'trade',
28530         '89l',
28531         'alefsym',
28532         '8cg',
28533         'larr',
28534         '8ch',
28535         'uarr',
28536         '8ci',
28537         'rarr',
28538         '8cj',
28539         'darr',
28540         '8ck',
28541         'harr',
28542         '8dl',
28543         'crarr',
28544         '8eg',
28545         'lArr',
28546         '8eh',
28547         'uArr',
28548         '8ei',
28549         'rArr',
28550         '8ej',
28551         'dArr',
28552         '8ek',
28553         'hArr',
28554         '8g0',
28555         'forall',
28556         '8g2',
28557         'part',
28558         '8g3',
28559         'exist',
28560         '8g5',
28561         'empty',
28562         '8g7',
28563         'nabla',
28564         '8g8',
28565         'isin',
28566         '8g9',
28567         'notin',
28568         '8gb',
28569         'ni',
28570         '8gf',
28571         'prod',
28572         '8gh',
28573         'sum',
28574         '8gi',
28575         'minus',
28576         '8gn',
28577         'lowast',
28578         '8gq',
28579         'radic',
28580         '8gt',
28581         'prop',
28582         '8gu',
28583         'infin',
28584         '8h0',
28585         'ang',
28586         '8h7',
28587         'and',
28588         '8h8',
28589         'or',
28590         '8h9',
28591         'cap',
28592         '8ha',
28593         'cup',
28594         '8hb',
28595         'int',
28596         '8hk',
28597         'there4',
28598         '8hs',
28599         'sim',
28600         '8i5',
28601         'cong',
28602         '8i8',
28603         'asymp',
28604         '8j0',
28605         'ne',
28606         '8j1',
28607         'equiv',
28608         '8j4',
28609         'le',
28610         '8j5',
28611         'ge',
28612         '8k2',
28613         'sub',
28614         '8k3',
28615         'sup',
28616         '8k4',
28617         'nsub',
28618         '8k6',
28619         'sube',
28620         '8k7',
28621         'supe',
28622         '8kl',
28623         'oplus',
28624         '8kn',
28625         'otimes',
28626         '8l5',
28627         'perp',
28628         '8m5',
28629         'sdot',
28630         '8o8',
28631         'lceil',
28632         '8o9',
28633         'rceil',
28634         '8oa',
28635         'lfloor',
28636         '8ob',
28637         'rfloor',
28638         '8p9',
28639         'lang',
28640         '8pa',
28641         'rang',
28642         '9ea',
28643         'loz',
28644         '9j0',
28645         'spades',
28646         '9j3',
28647         'clubs',
28648         '9j5',
28649         'hearts',
28650         '9j6',
28651         'diams',
28652         'ai',
28653         'OElig',
28654         'aj',
28655         'oelig',
28656         'b0',
28657         'Scaron',
28658         'b1',
28659         'scaron',
28660         'bo',
28661         'Yuml',
28662         'm6',
28663         'circ',
28664         'ms',
28665         'tilde',
28666         '802',
28667         'ensp',
28668         '803',
28669         'emsp',
28670         '809',
28671         'thinsp',
28672         '80c',
28673         'zwnj',
28674         '80d',
28675         'zwj',
28676         '80e',
28677         'lrm',
28678         '80f',
28679         'rlm',
28680         '80j',
28681         'ndash',
28682         '80k',
28683         'mdash',
28684         '80o',
28685         'lsquo',
28686         '80p',
28687         'rsquo',
28688         '80q',
28689         'sbquo',
28690         '80s',
28691         'ldquo',
28692         '80t',
28693         'rdquo',
28694         '80u',
28695         'bdquo',
28696         '810',
28697         'dagger',
28698         '811',
28699         'Dagger',
28700         '81g',
28701         'permil',
28702         '81p',
28703         'lsaquo',
28704         '81q',
28705         'rsaquo',
28706         '85c',
28707         'euro'
28708     ],
28709
28710          
28711     /**
28712      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28713      *
28714      * @method encodeRaw
28715      * @param {String} text Text to encode.
28716      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28717      * @return {String} Entity encoded text.
28718      */
28719     encodeRaw: function(text, attr)
28720     {
28721         var t = this;
28722         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28723             return t.baseEntities[chr] || chr;
28724         });
28725     },
28726     /**
28727      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28728      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28729      * and is exposed as the DOMUtils.encode function.
28730      *
28731      * @method encodeAllRaw
28732      * @param {String} text Text to encode.
28733      * @return {String} Entity encoded text.
28734      */
28735     encodeAllRaw: function(text) {
28736         var t = this;
28737         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28738             return t.baseEntities[chr] || chr;
28739         });
28740     },
28741     /**
28742      * Encodes the specified string using numeric entities. The core entities will be
28743      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28744      *
28745      * @method encodeNumeric
28746      * @param {String} text Text to encode.
28747      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28748      * @return {String} Entity encoded text.
28749      */
28750     encodeNumeric: function(text, attr) {
28751         var t = this;
28752         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28753             // Multi byte sequence convert it to a single entity
28754             if (chr.length > 1) {
28755                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28756             }
28757             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28758         });
28759     },
28760     /**
28761      * Encodes the specified string using named entities. The core entities will be encoded
28762      * as named ones but all non lower ascii characters will be encoded into named entities.
28763      *
28764      * @method encodeNamed
28765      * @param {String} text Text to encode.
28766      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28767      * @param {Object} entities Optional parameter with entities to use.
28768      * @return {String} Entity encoded text.
28769      */
28770     encodeNamed: function(text, attr, entities) {
28771         var t = this;
28772         entities = entities || this.namedEntities;
28773         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28774             return t.baseEntities[chr] || entities[chr] || chr;
28775         });
28776     },
28777     /**
28778      * Returns an encode function based on the name(s) and it's optional entities.
28779      *
28780      * @method getEncodeFunc
28781      * @param {String} name Comma separated list of encoders for example named,numeric.
28782      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28783      * @return {function} Encode function to be used.
28784      */
28785     getEncodeFunc: function(name, entities) {
28786         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28787         var t = this;
28788         function encodeNamedAndNumeric(text, attr) {
28789             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28790                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28791             });
28792         }
28793
28794         function encodeCustomNamed(text, attr) {
28795             return t.encodeNamed(text, attr, entities);
28796         }
28797         // Replace + with , to be compatible with previous TinyMCE versions
28798         name = this.makeMap(name.replace(/\+/g, ','));
28799         // Named and numeric encoder
28800         if (name.named && name.numeric) {
28801             return this.encodeNamedAndNumeric;
28802         }
28803         // Named encoder
28804         if (name.named) {
28805             // Custom names
28806             if (entities) {
28807                 return encodeCustomNamed;
28808             }
28809             return this.encodeNamed;
28810         }
28811         // Numeric
28812         if (name.numeric) {
28813             return this.encodeNumeric;
28814         }
28815         // Raw encoder
28816         return this.encodeRaw;
28817     },
28818     /**
28819      * Decodes the specified string, this will replace entities with raw UTF characters.
28820      *
28821      * @method decode
28822      * @param {String} text Text to entity decode.
28823      * @return {String} Entity decoded string.
28824      */
28825     decode: function(text)
28826     {
28827         var  t = this;
28828         return text.replace(this.entityRegExp, function(all, numeric) {
28829             if (numeric) {
28830                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28831                 // Support upper UTF
28832                 if (numeric > 65535) {
28833                     numeric -= 65536;
28834                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28835                 }
28836                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28837             }
28838             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28839         });
28840     },
28841     nativeDecode : function (text) {
28842         return text;
28843     },
28844     makeMap : function (items, delim, map) {
28845                 var i;
28846                 items = items || [];
28847                 delim = delim || ',';
28848                 if (typeof items == "string") {
28849                         items = items.split(delim);
28850                 }
28851                 map = map || {};
28852                 i = items.length;
28853                 while (i--) {
28854                         map[items[i]] = {};
28855                 }
28856                 return map;
28857         }
28858 };
28859     
28860     
28861     
28862 Roo.htmleditor.TidyEntities.init();
28863 /**
28864  * @class Roo.htmleditor.KeyEnter
28865  * Handle Enter press..
28866  * @cfg {Roo.HtmlEditorCore} core the editor.
28867  * @constructor
28868  * Create a new Filter.
28869  * @param {Object} config Configuration options
28870  */
28871
28872
28873
28874
28875
28876 Roo.htmleditor.KeyEnter = function(cfg) {
28877     Roo.apply(this, cfg);
28878     // this does not actually call walk as it's really just a abstract class
28879  
28880     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28881 }
28882
28883 //Roo.htmleditor.KeyEnter.i = 0;
28884
28885
28886 Roo.htmleditor.KeyEnter.prototype = {
28887     
28888     core : false,
28889     
28890     keypress : function(e)
28891     {
28892         if (e.charCode != 13 && e.charCode != 10) {
28893             Roo.log([e.charCode,e]);
28894             return true;
28895         }
28896         e.preventDefault();
28897         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28898         var doc = this.core.doc;
28899           //add a new line
28900        
28901     
28902         var sel = this.core.getSelection();
28903         var range = sel.getRangeAt(0);
28904         var n = range.commonAncestorContainer;
28905         var pc = range.closest([ 'ol', 'ul']);
28906         var pli = range.closest('li');
28907         if (!pc || e.ctrlKey) {
28908             // on it list, or ctrl pressed.
28909             if (!e.ctrlKey) {
28910                 sel.insertNode('br', 'after'); 
28911             } else {
28912                 // only do this if we have ctrl key..
28913                 var br = doc.createElement('br');
28914                 br.className = 'clear';
28915                 br.setAttribute('style', 'clear: both');
28916                 sel.insertNode(br, 'after'); 
28917             }
28918             
28919          
28920             this.core.undoManager.addEvent();
28921             this.core.fireEditorEvent(e);
28922             return false;
28923         }
28924         
28925         // deal with <li> insetion
28926         if (pli.innerText.trim() == '' &&
28927             pli.previousSibling &&
28928             pli.previousSibling.nodeName == 'LI' &&
28929             pli.previousSibling.innerText.trim() ==  '') {
28930             pli.parentNode.removeChild(pli.previousSibling);
28931             sel.cursorAfter(pc);
28932             this.core.undoManager.addEvent();
28933             this.core.fireEditorEvent(e);
28934             return false;
28935         }
28936     
28937         var li = doc.createElement('LI');
28938         li.innerHTML = '&nbsp;';
28939         if (!pli || !pli.firstSibling) {
28940             pc.appendChild(li);
28941         } else {
28942             pli.parentNode.insertBefore(li, pli.firstSibling);
28943         }
28944         sel.cursorText (li.firstChild);
28945       
28946         this.core.undoManager.addEvent();
28947         this.core.fireEditorEvent(e);
28948
28949         return false;
28950         
28951     
28952         
28953         
28954          
28955     }
28956 };
28957      
28958 /**
28959  * @class Roo.htmleditor.Block
28960  * Base class for html editor blocks - do not use it directly .. extend it..
28961  * @cfg {DomElement} node The node to apply stuff to.
28962  * @cfg {String} friendly_name the name that appears in the context bar about this block
28963  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28964  
28965  * @constructor
28966  * Create a new Filter.
28967  * @param {Object} config Configuration options
28968  */
28969
28970 Roo.htmleditor.Block  = function(cfg)
28971 {
28972     // do nothing .. should not be called really.
28973 }
28974 /**
28975  * factory method to get the block from an element (using cache if necessary)
28976  * @static
28977  * @param {HtmlElement} the dom element
28978  */
28979 Roo.htmleditor.Block.factory = function(node)
28980 {
28981     var cc = Roo.htmleditor.Block.cache;
28982     var id = Roo.get(node).id;
28983     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28984         Roo.htmleditor.Block.cache[id].readElement(node);
28985         return Roo.htmleditor.Block.cache[id];
28986     }
28987     var db  = node.getAttribute('data-block');
28988     if (!db) {
28989         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28990     }
28991     var cls = Roo.htmleditor['Block' + db];
28992     if (typeof(cls) == 'undefined') {
28993         //Roo.log(node.getAttribute('data-block'));
28994         Roo.log("OOps missing block : " + 'Block' + db);
28995         return false;
28996     }
28997     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28998     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
28999 };
29000
29001 /**
29002  * initalize all Elements from content that are 'blockable'
29003  * @static
29004  * @param the body element
29005  */
29006 Roo.htmleditor.Block.initAll = function(body, type)
29007 {
29008     if (typeof(type) == 'undefined') {
29009         var ia = Roo.htmleditor.Block.initAll;
29010         ia(body,'table');
29011         ia(body,'td');
29012         ia(body,'figure');
29013         return;
29014     }
29015     Roo.each(Roo.get(body).query(type), function(e) {
29016         Roo.htmleditor.Block.factory(e);    
29017     },this);
29018 };
29019 // question goes here... do we need to clear out this cache sometimes?
29020 // or show we make it relivant to the htmleditor.
29021 Roo.htmleditor.Block.cache = {};
29022
29023 Roo.htmleditor.Block.prototype = {
29024     
29025     node : false,
29026     
29027      // used by context menu
29028     friendly_name : 'Based Block',
29029     
29030     // text for button to delete this element
29031     deleteTitle : false,
29032     
29033     context : false,
29034     /**
29035      * Update a node with values from this object
29036      * @param {DomElement} node
29037      */
29038     updateElement : function(node)
29039     {
29040         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
29041     },
29042      /**
29043      * convert to plain HTML for calling insertAtCursor..
29044      */
29045     toHTML : function()
29046     {
29047         return Roo.DomHelper.markup(this.toObject());
29048     },
29049     /**
29050      * used by readEleemnt to extract data from a node
29051      * may need improving as it's pretty basic
29052      
29053      * @param {DomElement} node
29054      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
29055      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
29056      * @param {String} style the style property - eg. text-align
29057      */
29058     getVal : function(node, tag, attr, style)
29059     {
29060         var n = node;
29061         if (tag !== true && n.tagName != tag.toUpperCase()) {
29062             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
29063             // but kiss for now.
29064             n = node.getElementsByTagName(tag).item(0);
29065         }
29066         if (!n) {
29067             return '';
29068         }
29069         if (attr === false) {
29070             return n;
29071         }
29072         if (attr == 'html') {
29073             return n.innerHTML;
29074         }
29075         if (attr == 'style') {
29076             return n.style[style]; 
29077         }
29078         
29079         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29080             
29081     },
29082     /**
29083      * create a DomHelper friendly object - for use with 
29084      * Roo.DomHelper.markup / overwrite / etc..
29085      * (override this)
29086      */
29087     toObject : function()
29088     {
29089         return {};
29090     },
29091       /**
29092      * Read a node that has a 'data-block' property - and extract the values from it.
29093      * @param {DomElement} node - the node
29094      */
29095     readElement : function(node)
29096     {
29097         
29098     } 
29099     
29100     
29101 };
29102
29103  
29104
29105 /**
29106  * @class Roo.htmleditor.BlockFigure
29107  * Block that has an image and a figcaption
29108  * @cfg {String} image_src the url for the image
29109  * @cfg {String} align (left|right) alignment for the block default left
29110  * @cfg {String} caption the text to appear below  (and in the alt tag)
29111  * @cfg {String} caption_display (block|none) display or not the caption
29112  * @cfg {String|number} image_width the width of the image number or %?
29113  * @cfg {String|number} image_height the height of the image number or %?
29114  * 
29115  * @constructor
29116  * Create a new Filter.
29117  * @param {Object} config Configuration options
29118  */
29119
29120 Roo.htmleditor.BlockFigure = function(cfg)
29121 {
29122     if (cfg.node) {
29123         this.readElement(cfg.node);
29124         this.updateElement(cfg.node);
29125     }
29126     Roo.apply(this, cfg);
29127 }
29128 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29129  
29130     
29131     // setable values.
29132     image_src: '',
29133     align: 'center',
29134     caption : '',
29135     caption_display : 'block',
29136     width : '100%',
29137     cls : '',
29138     href: '',
29139     video_url : '',
29140     
29141     // margin: '2%', not used
29142     
29143     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
29144
29145     
29146     // used by context menu
29147     friendly_name : 'Image with caption',
29148     deleteTitle : "Delete Image and Caption",
29149     
29150     contextMenu : function(toolbar)
29151     {
29152         
29153         var block = function() {
29154             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29155         };
29156         
29157         
29158         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29159         
29160         var syncValue = toolbar.editorcore.syncValue;
29161         
29162         var fields = {};
29163         
29164         return [
29165              {
29166                 xtype : 'TextItem',
29167                 text : "Source: ",
29168                 xns : rooui.Toolbar  //Boostrap?
29169             },
29170             {
29171                 xtype : 'Button',
29172                 text: 'Change Image URL',
29173                  
29174                 listeners : {
29175                     click: function (btn, state)
29176                     {
29177                         var b = block();
29178                         
29179                         Roo.MessageBox.show({
29180                             title : "Image Source URL",
29181                             msg : "Enter the url for the image",
29182                             buttons: Roo.MessageBox.OKCANCEL,
29183                             fn: function(btn, val){
29184                                 if (btn != 'ok') {
29185                                     return;
29186                                 }
29187                                 b.image_src = val;
29188                                 b.updateElement();
29189                                 syncValue();
29190                                 toolbar.editorcore.onEditorEvent();
29191                             },
29192                             minWidth:250,
29193                             prompt:true,
29194                             //multiline: multiline,
29195                             modal : true,
29196                             value : b.image_src
29197                         });
29198                     }
29199                 },
29200                 xns : rooui.Toolbar
29201             },
29202          
29203             {
29204                 xtype : 'Button',
29205                 text: 'Change Link URL',
29206                  
29207                 listeners : {
29208                     click: function (btn, state)
29209                     {
29210                         var b = block();
29211                         
29212                         Roo.MessageBox.show({
29213                             title : "Link URL",
29214                             msg : "Enter the url for the link - leave blank to have no link",
29215                             buttons: Roo.MessageBox.OKCANCEL,
29216                             fn: function(btn, val){
29217                                 if (btn != 'ok') {
29218                                     return;
29219                                 }
29220                                 b.href = val;
29221                                 b.updateElement();
29222                                 syncValue();
29223                                 toolbar.editorcore.onEditorEvent();
29224                             },
29225                             minWidth:250,
29226                             prompt:true,
29227                             //multiline: multiline,
29228                             modal : true,
29229                             value : b.href
29230                         });
29231                     }
29232                 },
29233                 xns : rooui.Toolbar
29234             },
29235             {
29236                 xtype : 'Button',
29237                 text: 'Show Video URL',
29238                  
29239                 listeners : {
29240                     click: function (btn, state)
29241                     {
29242                         Roo.MessageBox.alert("Video URL",
29243                             block().video_url == '' ? 'This image is not linked ot a video' :
29244                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29245                     }
29246                 },
29247                 xns : rooui.Toolbar
29248             },
29249             
29250             
29251             {
29252                 xtype : 'TextItem',
29253                 text : "Width: ",
29254                 xns : rooui.Toolbar  //Boostrap?
29255             },
29256             {
29257                 xtype : 'ComboBox',
29258                 allowBlank : false,
29259                 displayField : 'val',
29260                 editable : true,
29261                 listWidth : 100,
29262                 triggerAction : 'all',
29263                 typeAhead : true,
29264                 valueField : 'val',
29265                 width : 70,
29266                 name : 'width',
29267                 listeners : {
29268                     select : function (combo, r, index)
29269                     {
29270                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29271                         var b = block();
29272                         b.width = r.get('val');
29273                         b.updateElement();
29274                         syncValue();
29275                         toolbar.editorcore.onEditorEvent();
29276                     }
29277                 },
29278                 xns : rooui.form,
29279                 store : {
29280                     xtype : 'SimpleStore',
29281                     data : [
29282                         ['100%'],
29283                         ['80%'],
29284                         ['50%'],
29285                         ['20%'],
29286                         ['10%']
29287                     ],
29288                     fields : [ 'val'],
29289                     xns : Roo.data
29290                 }
29291             },
29292             {
29293                 xtype : 'TextItem',
29294                 text : "Align: ",
29295                 xns : rooui.Toolbar  //Boostrap?
29296             },
29297             {
29298                 xtype : 'ComboBox',
29299                 allowBlank : false,
29300                 displayField : 'val',
29301                 editable : true,
29302                 listWidth : 100,
29303                 triggerAction : 'all',
29304                 typeAhead : true,
29305                 valueField : 'val',
29306                 width : 70,
29307                 name : 'align',
29308                 listeners : {
29309                     select : function (combo, r, index)
29310                     {
29311                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29312                         var b = block();
29313                         b.align = r.get('val');
29314                         b.updateElement();
29315                         syncValue();
29316                         toolbar.editorcore.onEditorEvent();
29317                     }
29318                 },
29319                 xns : rooui.form,
29320                 store : {
29321                     xtype : 'SimpleStore',
29322                     data : [
29323                         ['left'],
29324                         ['right'],
29325                         ['center']
29326                     ],
29327                     fields : [ 'val'],
29328                     xns : Roo.data
29329                 }
29330             },
29331             
29332             
29333             {
29334                 xtype : 'Button',
29335                 text: 'Hide Caption',
29336                 name : 'caption_display',
29337                 pressed : false,
29338                 enableToggle : true,
29339                 setValue : function(v) {
29340                     // this trigger toggle.
29341                      
29342                     this.setText(v ? "Hide Caption" : "Show Caption");
29343                     this.setPressed(v != 'block');
29344                 },
29345                 listeners : {
29346                     toggle: function (btn, state)
29347                     {
29348                         var b  = block();
29349                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29350                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29351                         b.updateElement();
29352                         syncValue();
29353                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29354                         toolbar.editorcore.onEditorEvent();
29355                     }
29356                 },
29357                 xns : rooui.Toolbar
29358             }
29359         ];
29360         
29361     },
29362     /**
29363      * create a DomHelper friendly object - for use with
29364      * Roo.DomHelper.markup / overwrite / etc..
29365      */
29366     toObject : function()
29367     {
29368         var d = document.createElement('div');
29369         d.innerHTML = this.caption;
29370         
29371         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
29372         
29373         var iw = this.align == 'center' ? this.width : '100%';
29374         var img =   {
29375             tag : 'img',
29376             contenteditable : 'false',
29377             src : this.image_src,
29378             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29379             style: {
29380                 width : iw,
29381                 maxWidth : iw + ' !important', // this is not getting rendered?
29382                 margin : m  
29383                 
29384             }
29385         };
29386         /*
29387         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29388                     '<a href="{2}">' + 
29389                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
29390                     '</a>' + 
29391                 '</div>',
29392         */
29393                 
29394         if (this.href.length > 0) {
29395             img = {
29396                 tag : 'a',
29397                 href: this.href,
29398                 contenteditable : 'true',
29399                 cn : [
29400                     img
29401                 ]
29402             };
29403         }
29404         
29405         
29406         if (this.video_url.length > 0) {
29407             img = {
29408                 tag : 'div',
29409                 cls : this.cls,
29410                 frameborder : 0,
29411                 allowfullscreen : true,
29412                 width : 420,  // these are for video tricks - that we replace the outer
29413                 height : 315,
29414                 src : this.video_url,
29415                 cn : [
29416                     img
29417                 ]
29418             };
29419         }
29420         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29421         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29422         
29423   
29424         var ret =   {
29425             tag: 'figure',
29426             'data-block' : 'Figure',
29427             'data-width' : this.width, 
29428             contenteditable : 'false',
29429             
29430             style : {
29431                 display: 'block',
29432                 float :  this.align ,
29433                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29434                 width : this.align == 'center' ? '100%' : this.width,
29435                 margin:  '0px',
29436                 padding: this.align == 'center' ? '0' : '0 10px' ,
29437                 textAlign : this.align   // seems to work for email..
29438                 
29439             },
29440            
29441             
29442             align : this.align,
29443             cn : [
29444                 img,
29445               
29446                 {
29447                     tag: 'figcaption',
29448                     'data-display' : this.caption_display,
29449                     style : {
29450                         textAlign : 'left',
29451                         fontSize : '16px',
29452                         lineHeight : '24px',
29453                         display : this.caption_display,
29454                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
29455                         margin: m,
29456                         width: this.align == 'center' ?  this.width : '100%' 
29457                     
29458                          
29459                     },
29460                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
29461                     cn : [
29462                         {
29463                             tag: 'div',
29464                             style  : {
29465                                 marginTop : '16px',
29466                                 textAlign : 'left'
29467                             },
29468                             align: 'left',
29469                             cn : [
29470                                 {
29471                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
29472                                     tag : 'i',
29473                                     contenteditable : true,
29474                                     html : captionhtml
29475                                 }
29476                                 
29477                             ]
29478                         }
29479                         
29480                     ]
29481                     
29482                 }
29483             ]
29484         };
29485         return ret;
29486          
29487     },
29488     
29489     readElement : function(node)
29490     {
29491         // this should not really come from the link...
29492         this.video_url = this.getVal(node, 'div', 'src');
29493         this.cls = this.getVal(node, 'div', 'class');
29494         this.href = this.getVal(node, 'a', 'href');
29495         
29496         
29497         this.image_src = this.getVal(node, 'img', 'src');
29498          
29499         this.align = this.getVal(node, 'figure', 'align');
29500         var figcaption = this.getVal(node, 'figcaption', false);
29501         if (figcaption !== '') {
29502             this.caption = this.getVal(figcaption, 'i', 'html');
29503         }
29504         
29505
29506         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29507         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29508         this.width = this.getVal(node, true, 'data-width');
29509         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29510         
29511     },
29512     removeNode : function()
29513     {
29514         return this.node;
29515     }
29516     
29517   
29518    
29519      
29520     
29521     
29522     
29523     
29524 })
29525
29526  
29527
29528 /**
29529  * @class Roo.htmleditor.BlockTable
29530  * Block that manages a table
29531  * 
29532  * @constructor
29533  * Create a new Filter.
29534  * @param {Object} config Configuration options
29535  */
29536
29537 Roo.htmleditor.BlockTable = function(cfg)
29538 {
29539     if (cfg.node) {
29540         this.readElement(cfg.node);
29541         this.updateElement(cfg.node);
29542     }
29543     Roo.apply(this, cfg);
29544     if (!cfg.node) {
29545         this.rows = [];
29546         for(var r = 0; r < this.no_row; r++) {
29547             this.rows[r] = [];
29548             for(var c = 0; c < this.no_col; c++) {
29549                 this.rows[r][c] = this.emptyCell();
29550             }
29551         }
29552     }
29553     
29554     
29555 }
29556 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29557  
29558     rows : false,
29559     no_col : 1,
29560     no_row : 1,
29561     
29562     
29563     width: '100%',
29564     
29565     // used by context menu
29566     friendly_name : 'Table',
29567     deleteTitle : 'Delete Table',
29568     // context menu is drawn once..
29569     
29570     contextMenu : function(toolbar)
29571     {
29572         
29573         var block = function() {
29574             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29575         };
29576         
29577         
29578         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29579         
29580         var syncValue = toolbar.editorcore.syncValue;
29581         
29582         var fields = {};
29583         
29584         return [
29585             {
29586                 xtype : 'TextItem',
29587                 text : "Width: ",
29588                 xns : rooui.Toolbar  //Boostrap?
29589             },
29590             {
29591                 xtype : 'ComboBox',
29592                 allowBlank : false,
29593                 displayField : 'val',
29594                 editable : true,
29595                 listWidth : 100,
29596                 triggerAction : 'all',
29597                 typeAhead : true,
29598                 valueField : 'val',
29599                 width : 100,
29600                 name : 'width',
29601                 listeners : {
29602                     select : function (combo, r, index)
29603                     {
29604                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29605                         var b = block();
29606                         b.width = r.get('val');
29607                         b.updateElement();
29608                         syncValue();
29609                         toolbar.editorcore.onEditorEvent();
29610                     }
29611                 },
29612                 xns : rooui.form,
29613                 store : {
29614                     xtype : 'SimpleStore',
29615                     data : [
29616                         ['100%'],
29617                         ['auto']
29618                     ],
29619                     fields : [ 'val'],
29620                     xns : Roo.data
29621                 }
29622             },
29623             // -------- Cols
29624             
29625             {
29626                 xtype : 'TextItem',
29627                 text : "Columns: ",
29628                 xns : rooui.Toolbar  //Boostrap?
29629             },
29630          
29631             {
29632                 xtype : 'Button',
29633                 text: '-',
29634                 listeners : {
29635                     click : function (_self, e)
29636                     {
29637                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29638                         block().removeColumn();
29639                         syncValue();
29640                         toolbar.editorcore.onEditorEvent();
29641                     }
29642                 },
29643                 xns : rooui.Toolbar
29644             },
29645             {
29646                 xtype : 'Button',
29647                 text: '+',
29648                 listeners : {
29649                     click : function (_self, e)
29650                     {
29651                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29652                         block().addColumn();
29653                         syncValue();
29654                         toolbar.editorcore.onEditorEvent();
29655                     }
29656                 },
29657                 xns : rooui.Toolbar
29658             },
29659             // -------- ROWS
29660             {
29661                 xtype : 'TextItem',
29662                 text : "Rows: ",
29663                 xns : rooui.Toolbar  //Boostrap?
29664             },
29665          
29666             {
29667                 xtype : 'Button',
29668                 text: '-',
29669                 listeners : {
29670                     click : function (_self, e)
29671                     {
29672                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29673                         block().removeRow();
29674                         syncValue();
29675                         toolbar.editorcore.onEditorEvent();
29676                     }
29677                 },
29678                 xns : rooui.Toolbar
29679             },
29680             {
29681                 xtype : 'Button',
29682                 text: '+',
29683                 listeners : {
29684                     click : function (_self, e)
29685                     {
29686                         block().addRow();
29687                         syncValue();
29688                         toolbar.editorcore.onEditorEvent();
29689                     }
29690                 },
29691                 xns : rooui.Toolbar
29692             },
29693             // -------- ROWS
29694             {
29695                 xtype : 'Button',
29696                 text: 'Reset Column Widths',
29697                 listeners : {
29698                     
29699                     click : function (_self, e)
29700                     {
29701                         block().resetWidths();
29702                         syncValue();
29703                         toolbar.editorcore.onEditorEvent();
29704                     }
29705                 },
29706                 xns : rooui.Toolbar
29707             } 
29708             
29709             
29710             
29711         ];
29712         
29713     },
29714     
29715     
29716   /**
29717      * create a DomHelper friendly object - for use with
29718      * Roo.DomHelper.markup / overwrite / etc..
29719      * ?? should it be called with option to hide all editing features?
29720      */
29721     toObject : function()
29722     {
29723         
29724         var ret = {
29725             tag : 'table',
29726             contenteditable : 'false', // this stops cell selection from picking the table.
29727             'data-block' : 'Table',
29728             style : {
29729                 width:  this.width,
29730                 border : 'solid 1px #000', // ??? hard coded?
29731                 'border-collapse' : 'collapse' 
29732             },
29733             cn : [
29734                 { tag : 'tbody' , cn : [] }
29735             ]
29736         };
29737         
29738         // do we have a head = not really 
29739         var ncols = 0;
29740         Roo.each(this.rows, function( row ) {
29741             var tr = {
29742                 tag: 'tr',
29743                 style : {
29744                     margin: '6px',
29745                     border : 'solid 1px #000',
29746                     textAlign : 'left' 
29747                 },
29748                 cn : [ ]
29749             };
29750             
29751             ret.cn[0].cn.push(tr);
29752             // does the row have any properties? ?? height?
29753             var nc = 0;
29754             Roo.each(row, function( cell ) {
29755                 
29756                 var td = {
29757                     tag : 'td',
29758                     contenteditable :  'true',
29759                     'data-block' : 'Td',
29760                     html : cell.html,
29761                     style : cell.style
29762                 };
29763                 if (cell.colspan > 1) {
29764                     td.colspan = cell.colspan ;
29765                     nc += cell.colspan;
29766                 } else {
29767                     nc++;
29768                 }
29769                 if (cell.rowspan > 1) {
29770                     td.rowspan = cell.rowspan ;
29771                 }
29772                 
29773                 
29774                 // widths ?
29775                 tr.cn.push(td);
29776                     
29777                 
29778             }, this);
29779             ncols = Math.max(nc, ncols);
29780             
29781             
29782         }, this);
29783         // add the header row..
29784         
29785         ncols++;
29786          
29787         
29788         return ret;
29789          
29790     },
29791     
29792     readElement : function(node)
29793     {
29794         node  = node ? node : this.node ;
29795         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29796         
29797         this.rows = [];
29798         this.no_row = 0;
29799         var trs = Array.from(node.rows);
29800         trs.forEach(function(tr) {
29801             var row =  [];
29802             this.rows.push(row);
29803             
29804             this.no_row++;
29805             var no_column = 0;
29806             Array.from(tr.cells).forEach(function(td) {
29807                 
29808                 var add = {
29809                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29810                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29811                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29812                     html : td.innerHTML
29813                 };
29814                 no_column += add.colspan;
29815                      
29816                 
29817                 row.push(add);
29818                 
29819                 
29820             },this);
29821             this.no_col = Math.max(this.no_col, no_column);
29822             
29823             
29824         },this);
29825         
29826         
29827     },
29828     normalizeRows: function()
29829     {
29830         var ret= [];
29831         var rid = -1;
29832         this.rows.forEach(function(row) {
29833             rid++;
29834             ret[rid] = [];
29835             row = this.normalizeRow(row);
29836             var cid = 0;
29837             row.forEach(function(c) {
29838                 while (typeof(ret[rid][cid]) != 'undefined') {
29839                     cid++;
29840                 }
29841                 if (typeof(ret[rid]) == 'undefined') {
29842                     ret[rid] = [];
29843                 }
29844                 ret[rid][cid] = c;
29845                 c.row = rid;
29846                 c.col = cid;
29847                 if (c.rowspan < 2) {
29848                     return;
29849                 }
29850                 
29851                 for(var i = 1 ;i < c.rowspan; i++) {
29852                     if (typeof(ret[rid+i]) == 'undefined') {
29853                         ret[rid+i] = [];
29854                     }
29855                     ret[rid+i][cid] = c;
29856                 }
29857             });
29858         }, this);
29859         return ret;
29860     
29861     },
29862     
29863     normalizeRow: function(row)
29864     {
29865         var ret= [];
29866         row.forEach(function(c) {
29867             if (c.colspan < 2) {
29868                 ret.push(c);
29869                 return;
29870             }
29871             for(var i =0 ;i < c.colspan; i++) {
29872                 ret.push(c);
29873             }
29874         });
29875         return ret;
29876     
29877     },
29878     
29879     deleteColumn : function(sel)
29880     {
29881         if (!sel || sel.type != 'col') {
29882             return;
29883         }
29884         if (this.no_col < 2) {
29885             return;
29886         }
29887         
29888         this.rows.forEach(function(row) {
29889             var cols = this.normalizeRow(row);
29890             var col = cols[sel.col];
29891             if (col.colspan > 1) {
29892                 col.colspan --;
29893             } else {
29894                 row.remove(col);
29895             }
29896             
29897         }, this);
29898         this.no_col--;
29899         
29900     },
29901     removeColumn : function()
29902     {
29903         this.deleteColumn({
29904             type: 'col',
29905             col : this.no_col-1
29906         });
29907         this.updateElement();
29908     },
29909     
29910      
29911     addColumn : function()
29912     {
29913         
29914         this.rows.forEach(function(row) {
29915             row.push(this.emptyCell());
29916            
29917         }, this);
29918         this.updateElement();
29919     },
29920     
29921     deleteRow : function(sel)
29922     {
29923         if (!sel || sel.type != 'row') {
29924             return;
29925         }
29926         
29927         if (this.no_row < 2) {
29928             return;
29929         }
29930         
29931         var rows = this.normalizeRows();
29932         
29933         
29934         rows[sel.row].forEach(function(col) {
29935             if (col.rowspan > 1) {
29936                 col.rowspan--;
29937             } else {
29938                 col.remove = 1; // flage it as removed.
29939             }
29940             
29941         }, this);
29942         var newrows = [];
29943         this.rows.forEach(function(row) {
29944             newrow = [];
29945             row.forEach(function(c) {
29946                 if (typeof(c.remove) == 'undefined') {
29947                     newrow.push(c);
29948                 }
29949                 
29950             });
29951             if (newrow.length > 0) {
29952                 newrows.push(row);
29953             }
29954         });
29955         this.rows =  newrows;
29956         
29957         
29958         
29959         this.no_row--;
29960         this.updateElement();
29961         
29962     },
29963     removeRow : function()
29964     {
29965         this.deleteRow({
29966             type: 'row',
29967             row : this.no_row-1
29968         });
29969         
29970     },
29971     
29972      
29973     addRow : function()
29974     {
29975         
29976         var row = [];
29977         for (var i = 0; i < this.no_col; i++ ) {
29978             
29979             row.push(this.emptyCell());
29980            
29981         }
29982         this.rows.push(row);
29983         this.updateElement();
29984         
29985     },
29986      
29987     // the default cell object... at present...
29988     emptyCell : function() {
29989         return (new Roo.htmleditor.BlockTd({})).toObject();
29990         
29991      
29992     },
29993     
29994     removeNode : function()
29995     {
29996         return this.node;
29997     },
29998     
29999     
30000     
30001     resetWidths : function()
30002     {
30003         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
30004             var nn = Roo.htmleditor.Block.factory(n);
30005             nn.width = '';
30006             nn.updateElement(n);
30007         });
30008     }
30009     
30010     
30011     
30012     
30013 })
30014
30015 /**
30016  *
30017  * editing a TD?
30018  *
30019  * since selections really work on the table cell, then editing really should work from there
30020  *
30021  * The original plan was to support merging etc... - but that may not be needed yet..
30022  *
30023  * So this simple version will support:
30024  *   add/remove cols
30025  *   adjust the width +/-
30026  *   reset the width...
30027  *   
30028  *
30029  */
30030
30031
30032  
30033
30034 /**
30035  * @class Roo.htmleditor.BlockTable
30036  * Block that manages a table
30037  * 
30038  * @constructor
30039  * Create a new Filter.
30040  * @param {Object} config Configuration options
30041  */
30042
30043 Roo.htmleditor.BlockTd = function(cfg)
30044 {
30045     if (cfg.node) {
30046         this.readElement(cfg.node);
30047         this.updateElement(cfg.node);
30048     }
30049     Roo.apply(this, cfg);
30050      
30051     
30052     
30053 }
30054 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
30055  
30056     node : false,
30057     
30058     width: '',
30059     textAlign : 'left',
30060     valign : 'top',
30061     
30062     colspan : 1,
30063     rowspan : 1,
30064     
30065     
30066     // used by context menu
30067     friendly_name : 'Table Cell',
30068     deleteTitle : false, // use our customer delete
30069     
30070     // context menu is drawn once..
30071     
30072     contextMenu : function(toolbar)
30073     {
30074         
30075         var cell = function() {
30076             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30077         };
30078         
30079         var table = function() {
30080             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30081         };
30082         
30083         var lr = false;
30084         var saveSel = function()
30085         {
30086             lr = toolbar.editorcore.getSelection().getRangeAt(0);
30087         }
30088         var restoreSel = function()
30089         {
30090             if (lr) {
30091                 (function() {
30092                     toolbar.editorcore.focus();
30093                     var cr = toolbar.editorcore.getSelection();
30094                     cr.removeAllRanges();
30095                     cr.addRange(lr);
30096                     toolbar.editorcore.onEditorEvent();
30097                 }).defer(10, this);
30098                 
30099                 
30100             }
30101         }
30102         
30103         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30104         
30105         var syncValue = toolbar.editorcore.syncValue;
30106         
30107         var fields = {};
30108         
30109         return [
30110             {
30111                 xtype : 'Button',
30112                 text : 'Edit Table',
30113                 listeners : {
30114                     click : function() {
30115                         var t = toolbar.tb.selectedNode.closest('table');
30116                         toolbar.editorcore.selectNode(t);
30117                         toolbar.editorcore.onEditorEvent();                        
30118                     }
30119                 }
30120                 
30121             },
30122               
30123            
30124              
30125             {
30126                 xtype : 'TextItem',
30127                 text : "Column Width: ",
30128                  xns : rooui.Toolbar 
30129                
30130             },
30131             {
30132                 xtype : 'Button',
30133                 text: '-',
30134                 listeners : {
30135                     click : function (_self, e)
30136                     {
30137                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30138                         cell().shrinkColumn();
30139                         syncValue();
30140                          toolbar.editorcore.onEditorEvent();
30141                     }
30142                 },
30143                 xns : rooui.Toolbar
30144             },
30145             {
30146                 xtype : 'Button',
30147                 text: '+',
30148                 listeners : {
30149                     click : function (_self, e)
30150                     {
30151                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30152                         cell().growColumn();
30153                         syncValue();
30154                         toolbar.editorcore.onEditorEvent();
30155                     }
30156                 },
30157                 xns : rooui.Toolbar
30158             },
30159             
30160             {
30161                 xtype : 'TextItem',
30162                 text : "Vertical Align: ",
30163                 xns : rooui.Toolbar  //Boostrap?
30164             },
30165             {
30166                 xtype : 'ComboBox',
30167                 allowBlank : false,
30168                 displayField : 'val',
30169                 editable : true,
30170                 listWidth : 100,
30171                 triggerAction : 'all',
30172                 typeAhead : true,
30173                 valueField : 'val',
30174                 width : 100,
30175                 name : 'valign',
30176                 listeners : {
30177                     select : function (combo, r, index)
30178                     {
30179                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30180                         var b = cell();
30181                         b.valign = r.get('val');
30182                         b.updateElement();
30183                         syncValue();
30184                         toolbar.editorcore.onEditorEvent();
30185                     }
30186                 },
30187                 xns : rooui.form,
30188                 store : {
30189                     xtype : 'SimpleStore',
30190                     data : [
30191                         ['top'],
30192                         ['middle'],
30193                         ['bottom'] // there are afew more... 
30194                     ],
30195                     fields : [ 'val'],
30196                     xns : Roo.data
30197                 }
30198             },
30199             
30200             {
30201                 xtype : 'TextItem',
30202                 text : "Merge Cells: ",
30203                  xns : rooui.Toolbar 
30204                
30205             },
30206             
30207             
30208             {
30209                 xtype : 'Button',
30210                 text: 'Right',
30211                 listeners : {
30212                     click : function (_self, e)
30213                     {
30214                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30215                         cell().mergeRight();
30216                         //block().growColumn();
30217                         syncValue();
30218                         toolbar.editorcore.onEditorEvent();
30219                     }
30220                 },
30221                 xns : rooui.Toolbar
30222             },
30223              
30224             {
30225                 xtype : 'Button',
30226                 text: 'Below',
30227                 listeners : {
30228                     click : function (_self, e)
30229                     {
30230                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30231                         cell().mergeBelow();
30232                         //block().growColumn();
30233                         syncValue();
30234                         toolbar.editorcore.onEditorEvent();
30235                     }
30236                 },
30237                 xns : rooui.Toolbar
30238             },
30239             {
30240                 xtype : 'TextItem',
30241                 text : "| ",
30242                  xns : rooui.Toolbar 
30243                
30244             },
30245             
30246             {
30247                 xtype : 'Button',
30248                 text: 'Split',
30249                 listeners : {
30250                     click : function (_self, e)
30251                     {
30252                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30253                         cell().split();
30254                         syncValue();
30255                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30256                         toolbar.editorcore.onEditorEvent();
30257                                              
30258                     }
30259                 },
30260                 xns : rooui.Toolbar
30261             },
30262             {
30263                 xtype : 'Fill',
30264                 xns : rooui.Toolbar 
30265                
30266             },
30267         
30268           
30269             {
30270                 xtype : 'Button',
30271                 text: 'Delete',
30272                  
30273                 xns : rooui.Toolbar,
30274                 menu : {
30275                     xtype : 'Menu',
30276                     xns : rooui.menu,
30277                     items : [
30278                         {
30279                             xtype : 'Item',
30280                             html: 'Column',
30281                             listeners : {
30282                                 click : function (_self, e)
30283                                 {
30284                                     var t = table();
30285                                     
30286                                     cell().deleteColumn();
30287                                     syncValue();
30288                                     toolbar.editorcore.selectNode(t.node);
30289                                     toolbar.editorcore.onEditorEvent();   
30290                                 }
30291                             },
30292                             xns : rooui.menu
30293                         },
30294                         {
30295                             xtype : 'Item',
30296                             html: 'Row',
30297                             listeners : {
30298                                 click : function (_self, e)
30299                                 {
30300                                     var t = table();
30301                                     cell().deleteRow();
30302                                     syncValue();
30303                                     
30304                                     toolbar.editorcore.selectNode(t.node);
30305                                     toolbar.editorcore.onEditorEvent();   
30306                                                          
30307                                 }
30308                             },
30309                             xns : rooui.menu
30310                         },
30311                        {
30312                             xtype : 'Separator',
30313                             xns : rooui.menu
30314                         },
30315                         {
30316                             xtype : 'Item',
30317                             html: 'Table',
30318                             listeners : {
30319                                 click : function (_self, e)
30320                                 {
30321                                     var t = table();
30322                                     var nn = t.node.nextSibling || t.node.previousSibling;
30323                                     t.node.parentNode.removeChild(t.node);
30324                                     if (nn) { 
30325                                         toolbar.editorcore.selectNode(nn, true);
30326                                     }
30327                                     toolbar.editorcore.onEditorEvent();   
30328                                                          
30329                                 }
30330                             },
30331                             xns : rooui.menu
30332                         }
30333                     ]
30334                 }
30335             }
30336             
30337             // align... << fixme
30338             
30339         ];
30340         
30341     },
30342     
30343     
30344   /**
30345      * create a DomHelper friendly object - for use with
30346      * Roo.DomHelper.markup / overwrite / etc..
30347      * ?? should it be called with option to hide all editing features?
30348      */
30349  /**
30350      * create a DomHelper friendly object - for use with
30351      * Roo.DomHelper.markup / overwrite / etc..
30352      * ?? should it be called with option to hide all editing features?
30353      */
30354     toObject : function()
30355     {
30356         var ret = {
30357             tag : 'td',
30358             contenteditable : 'true', // this stops cell selection from picking the table.
30359             'data-block' : 'Td',
30360             valign : this.valign,
30361             style : {  
30362                 'text-align' :  this.textAlign,
30363                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30364                 'border-collapse' : 'collapse',
30365                 padding : '6px', // 8 for desktop / 4 for mobile
30366                 'vertical-align': this.valign
30367             },
30368             html : this.html
30369         };
30370         if (this.width != '') {
30371             ret.width = this.width;
30372             ret.style.width = this.width;
30373         }
30374         
30375         
30376         if (this.colspan > 1) {
30377             ret.colspan = this.colspan ;
30378         } 
30379         if (this.rowspan > 1) {
30380             ret.rowspan = this.rowspan ;
30381         }
30382         
30383            
30384         
30385         return ret;
30386          
30387     },
30388     
30389     readElement : function(node)
30390     {
30391         node  = node ? node : this.node ;
30392         this.width = node.style.width;
30393         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30394         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30395         this.html = node.innerHTML;
30396         if (node.style.textAlign != '') {
30397             this.textAlign = node.style.textAlign;
30398         }
30399         
30400         
30401     },
30402      
30403     // the default cell object... at present...
30404     emptyCell : function() {
30405         return {
30406             colspan :  1,
30407             rowspan :  1,
30408             textAlign : 'left',
30409             html : "&nbsp;" // is this going to be editable now?
30410         };
30411      
30412     },
30413     
30414     removeNode : function()
30415     {
30416         return this.node.closest('table');
30417          
30418     },
30419     
30420     cellData : false,
30421     
30422     colWidths : false,
30423     
30424     toTableArray  : function()
30425     {
30426         var ret = [];
30427         var tab = this.node.closest('tr').closest('table');
30428         Array.from(tab.rows).forEach(function(r, ri){
30429             ret[ri] = [];
30430         });
30431         var rn = 0;
30432         this.colWidths = [];
30433         var all_auto = true;
30434         Array.from(tab.rows).forEach(function(r, ri){
30435             
30436             var cn = 0;
30437             Array.from(r.cells).forEach(function(ce, ci){
30438                 var c =  {
30439                     cell : ce,
30440                     row : rn,
30441                     col: cn,
30442                     colspan : ce.colSpan,
30443                     rowspan : ce.rowSpan
30444                 };
30445                 if (ce.isEqualNode(this.node)) {
30446                     this.cellData = c;
30447                 }
30448                 // if we have been filled up by a row?
30449                 if (typeof(ret[rn][cn]) != 'undefined') {
30450                     while(typeof(ret[rn][cn]) != 'undefined') {
30451                         cn++;
30452                     }
30453                     c.col = cn;
30454                 }
30455                 
30456                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30457                     this.colWidths[cn] =   ce.style.width;
30458                     if (this.colWidths[cn] != '') {
30459                         all_auto = false;
30460                     }
30461                 }
30462                 
30463                 
30464                 if (c.colspan < 2 && c.rowspan < 2 ) {
30465                     ret[rn][cn] = c;
30466                     cn++;
30467                     return;
30468                 }
30469                 for(var j = 0; j < c.rowspan; j++) {
30470                     if (typeof(ret[rn+j]) == 'undefined') {
30471                         continue; // we have a problem..
30472                     }
30473                     ret[rn+j][cn] = c;
30474                     for(var i = 0; i < c.colspan; i++) {
30475                         ret[rn+j][cn+i] = c;
30476                     }
30477                 }
30478                 
30479                 cn += c.colspan;
30480             }, this);
30481             rn++;
30482         }, this);
30483         
30484         // initalize widths.?
30485         // either all widths or no widths..
30486         if (all_auto) {
30487             this.colWidths[0] = false; // no widths flag.
30488         }
30489         
30490         
30491         return ret;
30492         
30493     },
30494     
30495     
30496     
30497     
30498     mergeRight: function()
30499     {
30500          
30501         // get the contents of the next cell along..
30502         var tr = this.node.closest('tr');
30503         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30504         if (i >= tr.childNodes.length - 1) {
30505             return; // no cells on right to merge with.
30506         }
30507         var table = this.toTableArray();
30508         
30509         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30510             return; // nothing right?
30511         }
30512         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30513         // right cell - must be same rowspan and on the same row.
30514         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30515             return; // right hand side is not same rowspan.
30516         }
30517         
30518         
30519         
30520         this.node.innerHTML += ' ' + rc.cell.innerHTML;
30521         tr.removeChild(rc.cell);
30522         this.colspan += rc.colspan;
30523         this.node.setAttribute('colspan', this.colspan);
30524
30525         var table = this.toTableArray();
30526         this.normalizeWidths(table);
30527         this.updateWidths(table);
30528     },
30529     
30530     
30531     mergeBelow : function()
30532     {
30533         var table = this.toTableArray();
30534         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30535             return; // no row below
30536         }
30537         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30538             return; // nothing right?
30539         }
30540         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30541         
30542         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30543             return; // right hand side is not same rowspan.
30544         }
30545         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30546         rc.cell.parentNode.removeChild(rc.cell);
30547         this.rowspan += rc.rowspan;
30548         this.node.setAttribute('rowspan', this.rowspan);
30549     },
30550     
30551     split: function()
30552     {
30553         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30554             return;
30555         }
30556         var table = this.toTableArray();
30557         var cd = this.cellData;
30558         this.rowspan = 1;
30559         this.colspan = 1;
30560         
30561         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30562              
30563             
30564             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30565                 if (r == cd.row && c == cd.col) {
30566                     this.node.removeAttribute('rowspan');
30567                     this.node.removeAttribute('colspan');
30568                 }
30569                  
30570                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30571                 ntd.removeAttribute('id'); 
30572                 ntd.style.width  = this.colWidths[c];
30573                 ntd.innerHTML = '';
30574                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30575             }
30576             
30577         }
30578         this.redrawAllCells(table);
30579         
30580     },
30581     
30582     
30583     
30584     redrawAllCells: function(table)
30585     {
30586         
30587          
30588         var tab = this.node.closest('tr').closest('table');
30589         var ctr = tab.rows[0].parentNode;
30590         Array.from(tab.rows).forEach(function(r, ri){
30591             
30592             Array.from(r.cells).forEach(function(ce, ci){
30593                 ce.parentNode.removeChild(ce);
30594             });
30595             r.parentNode.removeChild(r);
30596         });
30597         for(var r = 0 ; r < table.length; r++) {
30598             var re = tab.rows[r];
30599             
30600             var re = tab.ownerDocument.createElement('tr');
30601             ctr.appendChild(re);
30602             for(var c = 0 ; c < table[r].length; c++) {
30603                 if (table[r][c].cell === false) {
30604                     continue;
30605                 }
30606                 
30607                 re.appendChild(table[r][c].cell);
30608                  
30609                 table[r][c].cell = false;
30610             }
30611         }
30612         
30613     },
30614     updateWidths : function(table)
30615     {
30616         for(var r = 0 ; r < table.length; r++) {
30617            
30618             for(var c = 0 ; c < table[r].length; c++) {
30619                 if (table[r][c].cell === false) {
30620                     continue;
30621                 }
30622                 
30623                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30624                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30625                     el.width = Math.floor(this.colWidths[c])  +'%';
30626                     el.updateElement(el.node);
30627                 }
30628                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30629                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30630                     var width = 0;
30631                     for(var i = 0; i < table[r][c].colspan; i ++) {
30632                         width += Math.floor(this.colWidths[c + i]);
30633                     }
30634                     el.width = width  +'%';
30635                     el.updateElement(el.node);
30636                 }
30637                 table[r][c].cell = false; // done
30638             }
30639         }
30640     },
30641     normalizeWidths : function(table)
30642     {
30643         if (this.colWidths[0] === false) {
30644             var nw = 100.0 / this.colWidths.length;
30645             this.colWidths.forEach(function(w,i) {
30646                 this.colWidths[i] = nw;
30647             },this);
30648             return;
30649         }
30650     
30651         var t = 0, missing = [];
30652         
30653         this.colWidths.forEach(function(w,i) {
30654             //if you mix % and
30655             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30656             var add =  this.colWidths[i];
30657             if (add > 0) {
30658                 t+=add;
30659                 return;
30660             }
30661             missing.push(i);
30662             
30663             
30664         },this);
30665         var nc = this.colWidths.length;
30666         if (missing.length) {
30667             var mult = (nc - missing.length) / (1.0 * nc);
30668             var t = mult * t;
30669             var ew = (100 -t) / (1.0 * missing.length);
30670             this.colWidths.forEach(function(w,i) {
30671                 if (w > 0) {
30672                     this.colWidths[i] = w * mult;
30673                     return;
30674                 }
30675                 
30676                 this.colWidths[i] = ew;
30677             }, this);
30678             // have to make up numbers..
30679              
30680         }
30681         // now we should have all the widths..
30682         
30683     
30684     },
30685     
30686     shrinkColumn : function()
30687     {
30688         var table = this.toTableArray();
30689         this.normalizeWidths(table);
30690         var col = this.cellData.col;
30691         var nw = this.colWidths[col] * 0.8;
30692         if (nw < 5) {
30693             return;
30694         }
30695         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30696         this.colWidths.forEach(function(w,i) {
30697             if (i == col) {
30698                  this.colWidths[i] = nw;
30699                 return;
30700             }
30701             this.colWidths[i] += otherAdd
30702         }, this);
30703         this.updateWidths(table);
30704          
30705     },
30706     growColumn : function()
30707     {
30708         var table = this.toTableArray();
30709         this.normalizeWidths(table);
30710         var col = this.cellData.col;
30711         var nw = this.colWidths[col] * 1.2;
30712         if (nw > 90) {
30713             return;
30714         }
30715         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30716         this.colWidths.forEach(function(w,i) {
30717             if (i == col) {
30718                 this.colWidths[i] = nw;
30719                 return;
30720             }
30721             this.colWidths[i] -= otherSub
30722         }, this);
30723         this.updateWidths(table);
30724          
30725     },
30726     deleteRow : function()
30727     {
30728         // delete this rows 'tr'
30729         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30730         // then reduce the rowspan.
30731         var table = this.toTableArray();
30732         // this.cellData.row;
30733         for (var i =0;i< table[this.cellData.row].length ; i++) {
30734             var c = table[this.cellData.row][i];
30735             if (c.row != this.cellData.row) {
30736                 
30737                 c.rowspan--;
30738                 c.cell.setAttribute('rowspan', c.rowspan);
30739                 continue;
30740             }
30741             if (c.rowspan > 1) {
30742                 c.rowspan--;
30743                 c.cell.setAttribute('rowspan', c.rowspan);
30744             }
30745         }
30746         table.splice(this.cellData.row,1);
30747         this.redrawAllCells(table);
30748         
30749     },
30750     deleteColumn : function()
30751     {
30752         var table = this.toTableArray();
30753         
30754         for (var i =0;i< table.length ; i++) {
30755             var c = table[i][this.cellData.col];
30756             if (c.col != this.cellData.col) {
30757                 table[i][this.cellData.col].colspan--;
30758             } else if (c.colspan > 1) {
30759                 c.colspan--;
30760                 c.cell.setAttribute('colspan', c.colspan);
30761             }
30762             table[i].splice(this.cellData.col,1);
30763         }
30764         
30765         this.redrawAllCells(table);
30766     }
30767     
30768     
30769     
30770     
30771 })
30772
30773 //<script type="text/javascript">
30774
30775 /*
30776  * Based  Ext JS Library 1.1.1
30777  * Copyright(c) 2006-2007, Ext JS, LLC.
30778  * LGPL
30779  *
30780  */
30781  
30782 /**
30783  * @class Roo.HtmlEditorCore
30784  * @extends Roo.Component
30785  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30786  *
30787  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30788  */
30789
30790 Roo.HtmlEditorCore = function(config){
30791     
30792     
30793     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30794     
30795     
30796     this.addEvents({
30797         /**
30798          * @event initialize
30799          * Fires when the editor is fully initialized (including the iframe)
30800          * @param {Roo.HtmlEditorCore} this
30801          */
30802         initialize: true,
30803         /**
30804          * @event activate
30805          * Fires when the editor is first receives the focus. Any insertion must wait
30806          * until after this event.
30807          * @param {Roo.HtmlEditorCore} this
30808          */
30809         activate: true,
30810          /**
30811          * @event beforesync
30812          * Fires before the textarea is updated with content from the editor iframe. Return false
30813          * to cancel the sync.
30814          * @param {Roo.HtmlEditorCore} this
30815          * @param {String} html
30816          */
30817         beforesync: true,
30818          /**
30819          * @event beforepush
30820          * Fires before the iframe editor is updated with content from the textarea. Return false
30821          * to cancel the push.
30822          * @param {Roo.HtmlEditorCore} this
30823          * @param {String} html
30824          */
30825         beforepush: true,
30826          /**
30827          * @event sync
30828          * Fires when the textarea is updated with content from the editor iframe.
30829          * @param {Roo.HtmlEditorCore} this
30830          * @param {String} html
30831          */
30832         sync: true,
30833          /**
30834          * @event push
30835          * Fires when the iframe editor is updated with content from the textarea.
30836          * @param {Roo.HtmlEditorCore} this
30837          * @param {String} html
30838          */
30839         push: true,
30840         
30841         /**
30842          * @event editorevent
30843          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30844          * @param {Roo.HtmlEditorCore} this
30845          */
30846         editorevent: true 
30847          
30848         
30849     });
30850     
30851     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30852     
30853     // defaults : white / black...
30854     this.applyBlacklists();
30855     
30856     
30857     
30858 };
30859
30860
30861 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30862
30863
30864      /**
30865      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30866      */
30867     
30868     owner : false,
30869     
30870      /**
30871      * @cfg {String} css styling for resizing. (used on bootstrap only)
30872      */
30873     resize : false,
30874      /**
30875      * @cfg {Number} height (in pixels)
30876      */   
30877     height: 300,
30878    /**
30879      * @cfg {Number} width (in pixels)
30880      */   
30881     width: 500,
30882      /**
30883      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30884      *         if you are doing an email editor, this probably needs disabling, it's designed
30885      */
30886     autoClean: true,
30887     
30888     /**
30889      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30890      */
30891     enableBlocks : true,
30892     /**
30893      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30894      * 
30895      */
30896     stylesheets: false,
30897      /**
30898      * @cfg {String} language default en - language of text (usefull for rtl languages)
30899      * 
30900      */
30901     language: 'en',
30902     
30903     /**
30904      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30905      *          - by default they are stripped - if you are editing email you may need this.
30906      */
30907     allowComments: false,
30908     // id of frame..
30909     frameId: false,
30910     
30911     // private properties
30912     validationEvent : false,
30913     deferHeight: true,
30914     initialized : false,
30915     activated : false,
30916     sourceEditMode : false,
30917     onFocus : Roo.emptyFn,
30918     iframePad:3,
30919     hideMode:'offsets',
30920     
30921     clearUp: true,
30922     
30923     // blacklist + whitelisted elements..
30924     black: false,
30925     white: false,
30926      
30927     bodyCls : '',
30928
30929     
30930     undoManager : false,
30931     /**
30932      * Protected method that will not generally be called directly. It
30933      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30934      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30935      */
30936     getDocMarkup : function(){
30937         // body styles..
30938         var st = '';
30939         
30940         // inherit styels from page...?? 
30941         if (this.stylesheets === false) {
30942             
30943             Roo.get(document.head).select('style').each(function(node) {
30944                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30945             });
30946             
30947             Roo.get(document.head).select('link').each(function(node) { 
30948                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30949             });
30950             
30951         } else if (!this.stylesheets.length) {
30952                 // simple..
30953                 st = '<style type="text/css">' +
30954                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30955                    '</style>';
30956         } else {
30957             for (var i in this.stylesheets) {
30958                 if (typeof(this.stylesheets[i]) != 'string') {
30959                     continue;
30960                 }
30961                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30962             }
30963             
30964         }
30965         
30966         st +=  '<style type="text/css">' +
30967             'IMG { cursor: pointer } ' +
30968         '</style>';
30969         
30970         st += '<meta name="google" content="notranslate">';
30971         
30972         var cls = 'notranslate roo-htmleditor-body';
30973         
30974         if(this.bodyCls.length){
30975             cls += ' ' + this.bodyCls;
30976         }
30977         
30978         return '<html  class="notranslate" translate="no"><head>' + st  +
30979             //<style type="text/css">' +
30980             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30981             //'</style>' +
30982             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30983     },
30984
30985     // private
30986     onRender : function(ct, position)
30987     {
30988         var _t = this;
30989         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30990         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30991         
30992         
30993         this.el.dom.style.border = '0 none';
30994         this.el.dom.setAttribute('tabIndex', -1);
30995         this.el.addClass('x-hidden hide');
30996         
30997         
30998         
30999         if(Roo.isIE){ // fix IE 1px bogus margin
31000             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
31001         }
31002        
31003         
31004         this.frameId = Roo.id();
31005         
31006         var ifcfg = {
31007             tag: 'iframe',
31008             cls: 'form-control', // bootstrap..
31009             id: this.frameId,
31010             name: this.frameId,
31011             frameBorder : 'no',
31012             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
31013         };
31014         if (this.resize) {
31015             ifcfg.style = { resize : this.resize };
31016         }
31017         
31018         var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
31019         
31020         
31021         this.iframe = iframe.dom;
31022
31023         this.assignDocWin();
31024         
31025         this.doc.designMode = 'on';
31026        
31027         this.doc.open();
31028         this.doc.write(this.getDocMarkup());
31029         this.doc.close();
31030
31031         
31032         var task = { // must defer to wait for browser to be ready
31033             run : function(){
31034                 //console.log("run task?" + this.doc.readyState);
31035                 this.assignDocWin();
31036                 if(this.doc.body || this.doc.readyState == 'complete'){
31037                     try {
31038                         this.doc.designMode="on";
31039                         
31040                     } catch (e) {
31041                         return;
31042                     }
31043                     Roo.TaskMgr.stop(task);
31044                     this.initEditor.defer(10, this);
31045                 }
31046             },
31047             interval : 10,
31048             duration: 10000,
31049             scope: this
31050         };
31051         Roo.TaskMgr.start(task);
31052
31053     },
31054
31055     // private
31056     onResize : function(w, h)
31057     {
31058          Roo.log('resize: ' +w + ',' + h );
31059         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31060         if(!this.iframe){
31061             return;
31062         }
31063         if(typeof w == 'number'){
31064             
31065             this.iframe.style.width = w + 'px';
31066         }
31067         if(typeof h == 'number'){
31068             
31069             this.iframe.style.height = h + 'px';
31070             if(this.doc){
31071                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31072             }
31073         }
31074         
31075     },
31076
31077     /**
31078      * Toggles the editor between standard and source edit mode.
31079      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31080      */
31081     toggleSourceEdit : function(sourceEditMode){
31082         
31083         this.sourceEditMode = sourceEditMode === true;
31084         
31085         if(this.sourceEditMode){
31086  
31087             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
31088             
31089         }else{
31090             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31091             //this.iframe.className = '';
31092             this.deferFocus();
31093         }
31094         //this.setSize(this.owner.wrap.getSize());
31095         //this.fireEvent('editmodechange', this, this.sourceEditMode);
31096     },
31097
31098     
31099   
31100
31101     /**
31102      * Protected method that will not generally be called directly. If you need/want
31103      * custom HTML cleanup, this is the method you should override.
31104      * @param {String} html The HTML to be cleaned
31105      * return {String} The cleaned HTML
31106      */
31107     cleanHtml : function(html)
31108     {
31109         html = String(html);
31110         if(html.length > 5){
31111             if(Roo.isSafari){ // strip safari nonsense
31112                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31113             }
31114         }
31115         if(html == '&nbsp;'){
31116             html = '';
31117         }
31118         return html;
31119     },
31120
31121     /**
31122      * HTML Editor -> Textarea
31123      * Protected method that will not generally be called directly. Syncs the contents
31124      * of the editor iframe with the textarea.
31125      */
31126     syncValue : function()
31127     {
31128         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31129         if(this.initialized){
31130             
31131             if (this.undoManager) {
31132                 this.undoManager.addEvent();
31133             }
31134
31135             
31136             var bd = (this.doc.body || this.doc.documentElement);
31137            
31138             
31139             var sel = this.win.getSelection();
31140             
31141             var div = document.createElement('div');
31142             div.innerHTML = bd.innerHTML;
31143             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31144             if (gtx.length > 0) {
31145                 var rm = gtx.item(0).parentNode;
31146                 rm.parentNode.removeChild(rm);
31147             }
31148             
31149            
31150             if (this.enableBlocks) {
31151                 new Roo.htmleditor.FilterBlock({ node : div });
31152             }
31153             
31154             var html = div.innerHTML;
31155             
31156             //?? tidy?
31157             if (this.autoClean) {
31158                 
31159                 new Roo.htmleditor.FilterAttributes({
31160                     node : div,
31161                     attrib_white : [
31162                             'href',
31163                             'src',
31164                             'name',
31165                             'align',
31166                             'colspan',
31167                             'rowspan',
31168                             'data-display',
31169                             'data-width',
31170                             'start' ,
31171                             'style',
31172                             // youtube embed.
31173                             'class',
31174                             'allowfullscreen',
31175                             'frameborder',
31176                             'width',
31177                             'height',
31178                             'alt'
31179                             ],
31180                     attrib_clean : ['href', 'src' ] 
31181                 });
31182                 
31183                 var tidy = new Roo.htmleditor.TidySerializer({
31184                     inner:  true
31185                 });
31186                 html  = tidy.serialize(div);
31187                 
31188             }
31189             
31190             
31191             if(Roo.isSafari){
31192                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31193                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31194                 if(m && m[1]){
31195                     html = '<div style="'+m[0]+'">' + html + '</div>';
31196                 }
31197             }
31198             html = this.cleanHtml(html);
31199             // fix up the special chars.. normaly like back quotes in word...
31200             // however we do not want to do this with chinese..
31201             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31202                 
31203                 var cc = match.charCodeAt();
31204
31205                 // Get the character value, handling surrogate pairs
31206                 if (match.length == 2) {
31207                     // It's a surrogate pair, calculate the Unicode code point
31208                     var high = match.charCodeAt(0) - 0xD800;
31209                     var low  = match.charCodeAt(1) - 0xDC00;
31210                     cc = (high * 0x400) + low + 0x10000;
31211                 }  else if (
31212                     (cc >= 0x4E00 && cc < 0xA000 ) ||
31213                     (cc >= 0x3400 && cc < 0x4E00 ) ||
31214                     (cc >= 0xf900 && cc < 0xfb00 )
31215                 ) {
31216                         return match;
31217                 }  
31218          
31219                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31220                 return "&#" + cc + ";";
31221                 
31222                 
31223             });
31224             
31225             
31226              
31227             if(this.owner.fireEvent('beforesync', this, html) !== false){
31228                 this.el.dom.value = html;
31229                 this.owner.fireEvent('sync', this, html);
31230             }
31231         }
31232     },
31233
31234     /**
31235      * TEXTAREA -> EDITABLE
31236      * Protected method that will not generally be called directly. Pushes the value of the textarea
31237      * into the iframe editor.
31238      */
31239     pushValue : function()
31240     {
31241         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31242         if(this.initialized){
31243             var v = this.el.dom.value.trim();
31244             
31245             
31246             if(this.owner.fireEvent('beforepush', this, v) !== false){
31247                 var d = (this.doc.body || this.doc.documentElement);
31248                 d.innerHTML = v;
31249                  
31250                 this.el.dom.value = d.innerHTML;
31251                 this.owner.fireEvent('push', this, v);
31252             }
31253             if (this.autoClean) {
31254                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31255                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31256             }
31257             if (this.enableBlocks) {
31258                 Roo.htmleditor.Block.initAll(this.doc.body);
31259             }
31260             
31261             this.updateLanguage();
31262             
31263             var lc = this.doc.body.lastChild;
31264             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31265                 // add an extra line at the end.
31266                 this.doc.body.appendChild(this.doc.createElement('br'));
31267             }
31268             
31269             
31270         }
31271     },
31272
31273     // private
31274     deferFocus : function(){
31275         this.focus.defer(10, this);
31276     },
31277
31278     // doc'ed in Field
31279     focus : function(){
31280         if(this.win && !this.sourceEditMode){
31281             this.win.focus();
31282         }else{
31283             this.el.focus();
31284         }
31285     },
31286     
31287     assignDocWin: function()
31288     {
31289         var iframe = this.iframe;
31290         
31291          if(Roo.isIE){
31292             this.doc = iframe.contentWindow.document;
31293             this.win = iframe.contentWindow;
31294         } else {
31295 //            if (!Roo.get(this.frameId)) {
31296 //                return;
31297 //            }
31298 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31299 //            this.win = Roo.get(this.frameId).dom.contentWindow;
31300             
31301             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31302                 return;
31303             }
31304             
31305             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31306             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31307         }
31308     },
31309     
31310     // private
31311     initEditor : function(){
31312         //console.log("INIT EDITOR");
31313         this.assignDocWin();
31314         
31315         
31316         
31317         this.doc.designMode="on";
31318         this.doc.open();
31319         this.doc.write(this.getDocMarkup());
31320         this.doc.close();
31321         
31322         var dbody = (this.doc.body || this.doc.documentElement);
31323         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31324         // this copies styles from the containing element into thsi one..
31325         // not sure why we need all of this..
31326         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31327         
31328         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31329         //ss['background-attachment'] = 'fixed'; // w3c
31330         dbody.bgProperties = 'fixed'; // ie
31331         dbody.setAttribute("translate", "no");
31332         
31333         //Roo.DomHelper.applyStyles(dbody, ss);
31334         Roo.EventManager.on(this.doc, {
31335              
31336             'mouseup': this.onEditorEvent,
31337             'dblclick': this.onEditorEvent,
31338             'click': this.onEditorEvent,
31339             'keyup': this.onEditorEvent,
31340             
31341             buffer:100,
31342             scope: this
31343         });
31344         Roo.EventManager.on(this.doc, {
31345             'paste': this.onPasteEvent,
31346             scope : this
31347         });
31348         if(Roo.isGecko){
31349             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31350         }
31351         //??? needed???
31352         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31353             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31354         }
31355         this.initialized = true;
31356
31357         
31358         // initialize special key events - enter
31359         new Roo.htmleditor.KeyEnter({core : this});
31360         
31361          
31362         
31363         this.owner.fireEvent('initialize', this);
31364         this.pushValue();
31365     },
31366     // this is to prevent a href clicks resulting in a redirect?
31367    
31368     onPasteEvent : function(e,v)
31369     {
31370         // I think we better assume paste is going to be a dirty load of rubish from word..
31371         
31372         // even pasting into a 'email version' of this widget will have to clean up that mess.
31373         var cd = (e.browserEvent.clipboardData || window.clipboardData);
31374         
31375         // check what type of paste - if it's an image, then handle it differently.
31376         if (cd.files && cd.files.length > 0) {
31377             // pasting images?
31378             var urlAPI = (window.createObjectURL && window) || 
31379                 (window.URL && URL.revokeObjectURL && URL) || 
31380                 (window.webkitURL && webkitURL);
31381             
31382             var r = new FileReader();
31383             var t = this;
31384             r.addEventListener('load',function()
31385             {
31386                 
31387                 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
31388                 // is insert asycn?
31389                 if (t.enableBlocks) {
31390                     
31391                     Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31392                         if (img.closest('figure')) { // assume!! that it's aready
31393                             return;
31394                         }
31395                         var fig  = new Roo.htmleditor.BlockFigure({
31396                             image_src  : img.src
31397                         });
31398                         fig.updateElement(img); // replace it..
31399                         
31400                     });
31401                 }
31402                 t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31403                 t.owner.fireEvent('paste', this);
31404             });
31405             r.readAsDataURL(cd.files[0]);
31406             
31407             e.preventDefault();
31408             
31409             return false;
31410         }
31411         if (cd.types.indexOf('text/html') < 0 ) {
31412             return false;
31413         }
31414         var images = [];
31415         var html = cd.getData('text/html'); // clipboard event
31416         if (cd.types.indexOf('text/rtf') > -1) {
31417             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31418             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31419         }
31420         //Roo.log(images);
31421         //Roo.log(imgs);
31422         // fixme..
31423         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31424                        .map(function(g) { return g.toDataURL(); })
31425                        .filter(function(g) { return g != 'about:blank'; });
31426         
31427         //Roo.log(html);
31428         html = this.cleanWordChars(html);
31429         
31430         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31431         
31432         
31433         var sn = this.getParentElement();
31434         // check if d contains a table, and prevent nesting??
31435         //Roo.log(d.getElementsByTagName('table'));
31436         //Roo.log(sn);
31437         //Roo.log(sn.closest('table'));
31438         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31439             e.preventDefault();
31440             this.insertAtCursor("You can not nest tables");
31441             //Roo.log("prevent?"); // fixme - 
31442             return false;
31443         }
31444         
31445         
31446         
31447         if (images.length > 0) {
31448             // replace all v:imagedata - with img.
31449             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31450             Roo.each(ar, function(node) {
31451                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31452                 node.parentNode.removeChild(node);
31453             });
31454             
31455             
31456             Roo.each(d.getElementsByTagName('img'), function(img, i) {
31457                 img.setAttribute('src', images[i]);
31458             });
31459         }
31460         if (this.autoClean) {
31461             new Roo.htmleditor.FilterWord({ node : d });
31462             
31463             new Roo.htmleditor.FilterStyleToTag({ node : d });
31464             new Roo.htmleditor.FilterAttributes({
31465                 node : d,
31466                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31467                 attrib_clean : ['href', 'src' ] 
31468             });
31469             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31470             // should be fonts..
31471             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31472             new Roo.htmleditor.FilterParagraph({ node : d });
31473             new Roo.htmleditor.FilterSpan({ node : d });
31474             new Roo.htmleditor.FilterLongBr({ node : d });
31475             new Roo.htmleditor.FilterComment({ node : d });
31476             
31477             
31478         }
31479         if (this.enableBlocks) {
31480                 
31481             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31482                 if (img.closest('figure')) { // assume!! that it's aready
31483                     return;
31484                 }
31485                 var fig  = new Roo.htmleditor.BlockFigure({
31486                     image_src  : img.src
31487                 });
31488                 fig.updateElement(img); // replace it..
31489                 
31490             });
31491         }
31492         
31493         
31494         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31495         if (this.enableBlocks) {
31496             Roo.htmleditor.Block.initAll(this.doc.body);
31497         }
31498          
31499         
31500         e.preventDefault();
31501         this.owner.fireEvent('paste', this);
31502         return false;
31503         // default behaveiour should be our local cleanup paste? (optional?)
31504         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31505         //this.owner.fireEvent('paste', e, v);
31506     },
31507     // private
31508     onDestroy : function(){
31509         
31510         
31511         
31512         if(this.rendered){
31513             
31514             //for (var i =0; i < this.toolbars.length;i++) {
31515             //    // fixme - ask toolbars for heights?
31516             //    this.toolbars[i].onDestroy();
31517            // }
31518             
31519             //this.wrap.dom.innerHTML = '';
31520             //this.wrap.remove();
31521         }
31522     },
31523
31524     // private
31525     onFirstFocus : function(){
31526         
31527         this.assignDocWin();
31528         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31529         
31530         this.activated = true;
31531          
31532     
31533         if(Roo.isGecko){ // prevent silly gecko errors
31534             this.win.focus();
31535             var s = this.win.getSelection();
31536             if(!s.focusNode || s.focusNode.nodeType != 3){
31537                 var r = s.getRangeAt(0);
31538                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31539                 r.collapse(true);
31540                 this.deferFocus();
31541             }
31542             try{
31543                 this.execCmd('useCSS', true);
31544                 this.execCmd('styleWithCSS', false);
31545             }catch(e){}
31546         }
31547         this.owner.fireEvent('activate', this);
31548     },
31549
31550     // private
31551     adjustFont: function(btn){
31552         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31553         //if(Roo.isSafari){ // safari
31554         //    adjust *= 2;
31555        // }
31556         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31557         if(Roo.isSafari){ // safari
31558             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31559             v =  (v < 10) ? 10 : v;
31560             v =  (v > 48) ? 48 : v;
31561             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31562             
31563         }
31564         
31565         
31566         v = Math.max(1, v+adjust);
31567         
31568         this.execCmd('FontSize', v  );
31569     },
31570
31571     onEditorEvent : function(e)
31572     {
31573          
31574         
31575         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31576             return; // we do not handle this.. (undo manager does..)
31577         }
31578         // in theory this detects if the last element is not a br, then we try and do that.
31579         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31580         if (e &&
31581             e.target.nodeName == 'BODY' &&
31582             e.type == "mouseup" &&
31583             this.doc.body.lastChild
31584            ) {
31585             var lc = this.doc.body.lastChild;
31586             // gtx-trans is google translate plugin adding crap.
31587             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31588                 lc = lc.previousSibling;
31589             }
31590             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31591             // if last element is <BR> - then dont do anything.
31592             
31593                 var ns = this.doc.createElement('br');
31594                 this.doc.body.appendChild(ns);
31595                 range = this.doc.createRange();
31596                 range.setStartAfter(ns);
31597                 range.collapse(true);
31598                 var sel = this.win.getSelection();
31599                 sel.removeAllRanges();
31600                 sel.addRange(range);
31601             }
31602         }
31603         
31604         
31605         
31606         this.fireEditorEvent(e);
31607       //  this.updateToolbar();
31608         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31609     },
31610     
31611     fireEditorEvent: function(e)
31612     {
31613         this.owner.fireEvent('editorevent', this, e);
31614     },
31615
31616     insertTag : function(tg)
31617     {
31618         // could be a bit smarter... -> wrap the current selected tRoo..
31619         if (tg.toLowerCase() == 'span' ||
31620             tg.toLowerCase() == 'code' ||
31621             tg.toLowerCase() == 'sup' ||
31622             tg.toLowerCase() == 'sub' 
31623             ) {
31624             
31625             range = this.createRange(this.getSelection());
31626             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31627             wrappingNode.appendChild(range.extractContents());
31628             range.insertNode(wrappingNode);
31629
31630             return;
31631             
31632             
31633             
31634         }
31635         this.execCmd("formatblock",   tg);
31636         this.undoManager.addEvent(); 
31637     },
31638     
31639     insertText : function(txt)
31640     {
31641         
31642         
31643         var range = this.createRange();
31644         range.deleteContents();
31645                //alert(Sender.getAttribute('label'));
31646                
31647         range.insertNode(this.doc.createTextNode(txt));
31648         this.undoManager.addEvent();
31649     } ,
31650     
31651      
31652
31653     /**
31654      * Executes a Midas editor command on the editor document and performs necessary focus and
31655      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31656      * @param {String} cmd The Midas command
31657      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31658      */
31659     relayCmd : function(cmd, value)
31660     {
31661         
31662         switch (cmd) {
31663             case 'justifyleft':
31664             case 'justifyright':
31665             case 'justifycenter':
31666                 // if we are in a cell, then we will adjust the
31667                 var n = this.getParentElement();
31668                 var td = n.closest('td');
31669                 if (td) {
31670                     var bl = Roo.htmleditor.Block.factory(td);
31671                     bl.textAlign = cmd.replace('justify','');
31672                     bl.updateElement();
31673                     this.owner.fireEvent('editorevent', this);
31674                     return;
31675                 }
31676                 this.execCmd('styleWithCSS', true); // 
31677                 break;
31678             case 'bold':
31679             case 'italic':
31680                 // if there is no selection, then we insert, and set the curson inside it..
31681                 this.execCmd('styleWithCSS', false); 
31682                 break;
31683                 
31684         
31685             default:
31686                 break;
31687         }
31688         
31689         
31690         this.win.focus();
31691         this.execCmd(cmd, value);
31692         this.owner.fireEvent('editorevent', this);
31693         //this.updateToolbar();
31694         this.owner.deferFocus();
31695     },
31696
31697     /**
31698      * Executes a Midas editor command directly on the editor document.
31699      * For visual commands, you should use {@link #relayCmd} instead.
31700      * <b>This should only be called after the editor is initialized.</b>
31701      * @param {String} cmd The Midas command
31702      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31703      */
31704     execCmd : function(cmd, value){
31705         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31706         this.syncValue();
31707     },
31708  
31709  
31710    
31711     /**
31712      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31713      * to insert tRoo.
31714      * @param {String} text | dom node.. 
31715      */
31716     insertAtCursor : function(text)
31717     {
31718         
31719         if(!this.activated){
31720             return;
31721         }
31722          
31723         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31724             this.win.focus();
31725             
31726             
31727             // from jquery ui (MIT licenced)
31728             var range, node;
31729             var win = this.win;
31730             
31731             if (win.getSelection && win.getSelection().getRangeAt) {
31732                 
31733                 // delete the existing?
31734                 
31735                 this.createRange(this.getSelection()).deleteContents();
31736                 range = win.getSelection().getRangeAt(0);
31737                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31738                 range.insertNode(node);
31739                 range = range.cloneRange();
31740                 range.collapse(false);
31741                  
31742                 win.getSelection().removeAllRanges();
31743                 win.getSelection().addRange(range);
31744                 
31745                 
31746                 
31747             } else if (win.document.selection && win.document.selection.createRange) {
31748                 // no firefox support
31749                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31750                 win.document.selection.createRange().pasteHTML(txt);
31751             
31752             } else {
31753                 // no firefox support
31754                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31755                 this.execCmd('InsertHTML', txt);
31756             } 
31757             this.syncValue();
31758             
31759             this.deferFocus();
31760         }
31761     },
31762  // private
31763     mozKeyPress : function(e){
31764         if(e.ctrlKey){
31765             var c = e.getCharCode(), cmd;
31766           
31767             if(c > 0){
31768                 c = String.fromCharCode(c).toLowerCase();
31769                 switch(c){
31770                     case 'b':
31771                         cmd = 'bold';
31772                         break;
31773                     case 'i':
31774                         cmd = 'italic';
31775                         break;
31776                     
31777                     case 'u':
31778                         cmd = 'underline';
31779                         break;
31780                     
31781                     //case 'v':
31782                       //  this.cleanUpPaste.defer(100, this);
31783                       //  return;
31784                         
31785                 }
31786                 if(cmd){
31787                     
31788                     this.relayCmd(cmd);
31789                     //this.win.focus();
31790                     //this.execCmd(cmd);
31791                     //this.deferFocus();
31792                     e.preventDefault();
31793                 }
31794                 
31795             }
31796         }
31797     },
31798
31799     // private
31800     fixKeys : function(){ // load time branching for fastest keydown performance
31801         
31802         
31803         if(Roo.isIE){
31804             return function(e){
31805                 var k = e.getKey(), r;
31806                 if(k == e.TAB){
31807                     e.stopEvent();
31808                     r = this.doc.selection.createRange();
31809                     if(r){
31810                         r.collapse(true);
31811                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31812                         this.deferFocus();
31813                     }
31814                     return;
31815                 }
31816                 /// this is handled by Roo.htmleditor.KeyEnter
31817                  /*
31818                 if(k == e.ENTER){
31819                     r = this.doc.selection.createRange();
31820                     if(r){
31821                         var target = r.parentElement();
31822                         if(!target || target.tagName.toLowerCase() != 'li'){
31823                             e.stopEvent();
31824                             r.pasteHTML('<br/>');
31825                             r.collapse(false);
31826                             r.select();
31827                         }
31828                     }
31829                 }
31830                 */
31831                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31832                 //    this.cleanUpPaste.defer(100, this);
31833                 //    return;
31834                 //}
31835                 
31836                 
31837             };
31838         }else if(Roo.isOpera){
31839             return function(e){
31840                 var k = e.getKey();
31841                 if(k == e.TAB){
31842                     e.stopEvent();
31843                     this.win.focus();
31844                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31845                     this.deferFocus();
31846                 }
31847                
31848                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31849                 //    this.cleanUpPaste.defer(100, this);
31850                  //   return;
31851                 //}
31852                 
31853             };
31854         }else if(Roo.isSafari){
31855             return function(e){
31856                 var k = e.getKey();
31857                 
31858                 if(k == e.TAB){
31859                     e.stopEvent();
31860                     this.execCmd('InsertText','\t');
31861                     this.deferFocus();
31862                     return;
31863                 }
31864                  this.mozKeyPress(e);
31865                 
31866                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31867                  //   this.cleanUpPaste.defer(100, this);
31868                  //   return;
31869                // }
31870                 
31871              };
31872         }
31873     }(),
31874     
31875     getAllAncestors: function()
31876     {
31877         var p = this.getSelectedNode();
31878         var a = [];
31879         if (!p) {
31880             a.push(p); // push blank onto stack..
31881             p = this.getParentElement();
31882         }
31883         
31884         
31885         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31886             a.push(p);
31887             p = p.parentNode;
31888         }
31889         a.push(this.doc.body);
31890         return a;
31891     },
31892     lastSel : false,
31893     lastSelNode : false,
31894     
31895     
31896     getSelection : function() 
31897     {
31898         this.assignDocWin();
31899         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31900     },
31901     /**
31902      * Select a dom node
31903      * @param {DomElement} node the node to select
31904      */
31905     selectNode : function(node, collapse)
31906     {
31907         var nodeRange = node.ownerDocument.createRange();
31908         try {
31909             nodeRange.selectNode(node);
31910         } catch (e) {
31911             nodeRange.selectNodeContents(node);
31912         }
31913         if (collapse === true) {
31914             nodeRange.collapse(true);
31915         }
31916         //
31917         var s = this.win.getSelection();
31918         s.removeAllRanges();
31919         s.addRange(nodeRange);
31920     },
31921     
31922     getSelectedNode: function() 
31923     {
31924         // this may only work on Gecko!!!
31925         
31926         // should we cache this!!!!
31927         
31928          
31929          
31930         var range = this.createRange(this.getSelection()).cloneRange();
31931         
31932         if (Roo.isIE) {
31933             var parent = range.parentElement();
31934             while (true) {
31935                 var testRange = range.duplicate();
31936                 testRange.moveToElementText(parent);
31937                 if (testRange.inRange(range)) {
31938                     break;
31939                 }
31940                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31941                     break;
31942                 }
31943                 parent = parent.parentElement;
31944             }
31945             return parent;
31946         }
31947         
31948         // is ancestor a text element.
31949         var ac =  range.commonAncestorContainer;
31950         if (ac.nodeType == 3) {
31951             ac = ac.parentNode;
31952         }
31953         
31954         var ar = ac.childNodes;
31955          
31956         var nodes = [];
31957         var other_nodes = [];
31958         var has_other_nodes = false;
31959         for (var i=0;i<ar.length;i++) {
31960             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31961                 continue;
31962             }
31963             // fullly contained node.
31964             
31965             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31966                 nodes.push(ar[i]);
31967                 continue;
31968             }
31969             
31970             // probably selected..
31971             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31972                 other_nodes.push(ar[i]);
31973                 continue;
31974             }
31975             // outer..
31976             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31977                 continue;
31978             }
31979             
31980             
31981             has_other_nodes = true;
31982         }
31983         if (!nodes.length && other_nodes.length) {
31984             nodes= other_nodes;
31985         }
31986         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31987             return false;
31988         }
31989         
31990         return nodes[0];
31991     },
31992     
31993     
31994     createRange: function(sel)
31995     {
31996         // this has strange effects when using with 
31997         // top toolbar - not sure if it's a great idea.
31998         //this.editor.contentWindow.focus();
31999         if (typeof sel != "undefined") {
32000             try {
32001                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
32002             } catch(e) {
32003                 return this.doc.createRange();
32004             }
32005         } else {
32006             return this.doc.createRange();
32007         }
32008     },
32009     getParentElement: function()
32010     {
32011         
32012         this.assignDocWin();
32013         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
32014         
32015         var range = this.createRange(sel);
32016          
32017         try {
32018             var p = range.commonAncestorContainer;
32019             while (p.nodeType == 3) { // text node
32020                 p = p.parentNode;
32021             }
32022             return p;
32023         } catch (e) {
32024             return null;
32025         }
32026     
32027     },
32028     /***
32029      *
32030      * Range intersection.. the hard stuff...
32031      *  '-1' = before
32032      *  '0' = hits..
32033      *  '1' = after.
32034      *         [ -- selected range --- ]
32035      *   [fail]                        [fail]
32036      *
32037      *    basically..
32038      *      if end is before start or  hits it. fail.
32039      *      if start is after end or hits it fail.
32040      *
32041      *   if either hits (but other is outside. - then it's not 
32042      *   
32043      *    
32044      **/
32045     
32046     
32047     // @see http://www.thismuchiknow.co.uk/?p=64.
32048     rangeIntersectsNode : function(range, node)
32049     {
32050         var nodeRange = node.ownerDocument.createRange();
32051         try {
32052             nodeRange.selectNode(node);
32053         } catch (e) {
32054             nodeRange.selectNodeContents(node);
32055         }
32056     
32057         var rangeStartRange = range.cloneRange();
32058         rangeStartRange.collapse(true);
32059     
32060         var rangeEndRange = range.cloneRange();
32061         rangeEndRange.collapse(false);
32062     
32063         var nodeStartRange = nodeRange.cloneRange();
32064         nodeStartRange.collapse(true);
32065     
32066         var nodeEndRange = nodeRange.cloneRange();
32067         nodeEndRange.collapse(false);
32068     
32069         return rangeStartRange.compareBoundaryPoints(
32070                  Range.START_TO_START, nodeEndRange) == -1 &&
32071                rangeEndRange.compareBoundaryPoints(
32072                  Range.START_TO_START, nodeStartRange) == 1;
32073         
32074          
32075     },
32076     rangeCompareNode : function(range, node)
32077     {
32078         var nodeRange = node.ownerDocument.createRange();
32079         try {
32080             nodeRange.selectNode(node);
32081         } catch (e) {
32082             nodeRange.selectNodeContents(node);
32083         }
32084         
32085         
32086         range.collapse(true);
32087     
32088         nodeRange.collapse(true);
32089      
32090         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32091         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
32092          
32093         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32094         
32095         var nodeIsBefore   =  ss == 1;
32096         var nodeIsAfter    = ee == -1;
32097         
32098         if (nodeIsBefore && nodeIsAfter) {
32099             return 0; // outer
32100         }
32101         if (!nodeIsBefore && nodeIsAfter) {
32102             return 1; //right trailed.
32103         }
32104         
32105         if (nodeIsBefore && !nodeIsAfter) {
32106             return 2;  // left trailed.
32107         }
32108         // fully contined.
32109         return 3;
32110     },
32111  
32112     cleanWordChars : function(input) {// change the chars to hex code
32113         
32114        var swapCodes  = [ 
32115             [    8211, "&#8211;" ], 
32116             [    8212, "&#8212;" ], 
32117             [    8216,  "'" ],  
32118             [    8217, "'" ],  
32119             [    8220, '"' ],  
32120             [    8221, '"' ],  
32121             [    8226, "*" ],  
32122             [    8230, "..." ]
32123         ]; 
32124         var output = input;
32125         Roo.each(swapCodes, function(sw) { 
32126             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32127             
32128             output = output.replace(swapper, sw[1]);
32129         });
32130         
32131         return output;
32132     },
32133     
32134      
32135     
32136         
32137     
32138     cleanUpChild : function (node)
32139     {
32140         
32141         new Roo.htmleditor.FilterComment({node : node});
32142         new Roo.htmleditor.FilterAttributes({
32143                 node : node,
32144                 attrib_black : this.ablack,
32145                 attrib_clean : this.aclean,
32146                 style_white : this.cwhite,
32147                 style_black : this.cblack
32148         });
32149         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32150         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32151          
32152         
32153     },
32154     
32155     /**
32156      * Clean up MS wordisms...
32157      * @deprecated - use filter directly
32158      */
32159     cleanWord : function(node)
32160     {
32161         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32162         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32163         
32164     },
32165    
32166     
32167     /**
32168
32169      * @deprecated - use filters
32170      */
32171     cleanTableWidths : function(node)
32172     {
32173         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32174         
32175  
32176     },
32177     
32178      
32179         
32180     applyBlacklists : function()
32181     {
32182         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
32183         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
32184         
32185         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
32186         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
32187         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
32188         
32189         this.white = [];
32190         this.black = [];
32191         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32192             if (b.indexOf(tag) > -1) {
32193                 return;
32194             }
32195             this.white.push(tag);
32196             
32197         }, this);
32198         
32199         Roo.each(w, function(tag) {
32200             if (b.indexOf(tag) > -1) {
32201                 return;
32202             }
32203             if (this.white.indexOf(tag) > -1) {
32204                 return;
32205             }
32206             this.white.push(tag);
32207             
32208         }, this);
32209         
32210         
32211         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32212             if (w.indexOf(tag) > -1) {
32213                 return;
32214             }
32215             this.black.push(tag);
32216             
32217         }, this);
32218         
32219         Roo.each(b, function(tag) {
32220             if (w.indexOf(tag) > -1) {
32221                 return;
32222             }
32223             if (this.black.indexOf(tag) > -1) {
32224                 return;
32225             }
32226             this.black.push(tag);
32227             
32228         }, this);
32229         
32230         
32231         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
32232         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
32233         
32234         this.cwhite = [];
32235         this.cblack = [];
32236         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32237             if (b.indexOf(tag) > -1) {
32238                 return;
32239             }
32240             this.cwhite.push(tag);
32241             
32242         }, this);
32243         
32244         Roo.each(w, function(tag) {
32245             if (b.indexOf(tag) > -1) {
32246                 return;
32247             }
32248             if (this.cwhite.indexOf(tag) > -1) {
32249                 return;
32250             }
32251             this.cwhite.push(tag);
32252             
32253         }, this);
32254         
32255         
32256         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32257             if (w.indexOf(tag) > -1) {
32258                 return;
32259             }
32260             this.cblack.push(tag);
32261             
32262         }, this);
32263         
32264         Roo.each(b, function(tag) {
32265             if (w.indexOf(tag) > -1) {
32266                 return;
32267             }
32268             if (this.cblack.indexOf(tag) > -1) {
32269                 return;
32270             }
32271             this.cblack.push(tag);
32272             
32273         }, this);
32274     },
32275     
32276     setStylesheets : function(stylesheets)
32277     {
32278         if(typeof(stylesheets) == 'string'){
32279             Roo.get(this.iframe.contentDocument.head).createChild({
32280                 tag : 'link',
32281                 rel : 'stylesheet',
32282                 type : 'text/css',
32283                 href : stylesheets
32284             });
32285             
32286             return;
32287         }
32288         var _this = this;
32289      
32290         Roo.each(stylesheets, function(s) {
32291             if(!s.length){
32292                 return;
32293             }
32294             
32295             Roo.get(_this.iframe.contentDocument.head).createChild({
32296                 tag : 'link',
32297                 rel : 'stylesheet',
32298                 type : 'text/css',
32299                 href : s
32300             });
32301         });
32302
32303         
32304     },
32305     
32306     
32307     updateLanguage : function()
32308     {
32309         if (!this.iframe || !this.iframe.contentDocument) {
32310             return;
32311         }
32312         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32313     },
32314     
32315     
32316     removeStylesheets : function()
32317     {
32318         var _this = this;
32319         
32320         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32321             s.remove();
32322         });
32323     },
32324     
32325     setStyle : function(style)
32326     {
32327         Roo.get(this.iframe.contentDocument.head).createChild({
32328             tag : 'style',
32329             type : 'text/css',
32330             html : style
32331         });
32332
32333         return;
32334     }
32335     
32336     // hide stuff that is not compatible
32337     /**
32338      * @event blur
32339      * @hide
32340      */
32341     /**
32342      * @event change
32343      * @hide
32344      */
32345     /**
32346      * @event focus
32347      * @hide
32348      */
32349     /**
32350      * @event specialkey
32351      * @hide
32352      */
32353     /**
32354      * @cfg {String} fieldClass @hide
32355      */
32356     /**
32357      * @cfg {String} focusClass @hide
32358      */
32359     /**
32360      * @cfg {String} autoCreate @hide
32361      */
32362     /**
32363      * @cfg {String} inputType @hide
32364      */
32365     /**
32366      * @cfg {String} invalidClass @hide
32367      */
32368     /**
32369      * @cfg {String} invalidText @hide
32370      */
32371     /**
32372      * @cfg {String} msgFx @hide
32373      */
32374     /**
32375      * @cfg {String} validateOnBlur @hide
32376      */
32377 });
32378
32379 Roo.HtmlEditorCore.white = [
32380         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32381         
32382        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
32383        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
32384        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
32385        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
32386        'TABLE',   'UL',         'XMP', 
32387        
32388        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
32389       'THEAD',   'TR', 
32390      
32391       'DIR', 'MENU', 'OL', 'UL', 'DL',
32392        
32393       'EMBED',  'OBJECT'
32394 ];
32395
32396
32397 Roo.HtmlEditorCore.black = [
32398     //    'embed',  'object', // enable - backend responsiblity to clean thiese
32399         'APPLET', // 
32400         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
32401         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
32402         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
32403         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
32404         //'FONT' // CLEAN LATER..
32405         'COLGROUP', 'COL'   // messy tables.
32406         
32407         
32408 ];
32409 Roo.HtmlEditorCore.clean = [ // ?? needed???
32410      'SCRIPT', 'STYLE', 'TITLE', 'XML'
32411 ];
32412 Roo.HtmlEditorCore.tag_remove = [
32413     'FONT', 'TBODY'  
32414 ];
32415 // attributes..
32416
32417 Roo.HtmlEditorCore.ablack = [
32418     'on'
32419 ];
32420     
32421 Roo.HtmlEditorCore.aclean = [ 
32422     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
32423 ];
32424
32425 // protocols..
32426 Roo.HtmlEditorCore.pwhite= [
32427         'http',  'https',  'mailto'
32428 ];
32429
32430 // white listed style attributes.
32431 Roo.HtmlEditorCore.cwhite= [
32432       //  'text-align', /// default is to allow most things..
32433       
32434          
32435 //        'font-size'//??
32436 ];
32437
32438 // black listed style attributes.
32439 Roo.HtmlEditorCore.cblack= [
32440       //  'font-size' -- this can be set by the project 
32441 ];
32442
32443
32444
32445
32446     /*
32447  * - LGPL
32448  *
32449  * HtmlEditor
32450  * 
32451  */
32452
32453 /**
32454  * @class Roo.bootstrap.form.HtmlEditor
32455  * @extends Roo.bootstrap.form.TextArea
32456  * Bootstrap HtmlEditor class
32457
32458  * @constructor
32459  * Create a new HtmlEditor
32460  * @param {Object} config The config object
32461  */
32462
32463 Roo.bootstrap.form.HtmlEditor = function(config){
32464     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32465     if (!this.toolbars) {
32466         this.toolbars = [];
32467     }
32468     
32469     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32470     this.addEvents({
32471             /**
32472              * @event initialize
32473              * Fires when the editor is fully initialized (including the iframe)
32474              * @param {HtmlEditor} this
32475              */
32476             initialize: true,
32477             /**
32478              * @event activate
32479              * Fires when the editor is first receives the focus. Any insertion must wait
32480              * until after this event.
32481              * @param {HtmlEditor} this
32482              */
32483             activate: true,
32484              /**
32485              * @event beforesync
32486              * Fires before the textarea is updated with content from the editor iframe. Return false
32487              * to cancel the sync.
32488              * @param {HtmlEditor} this
32489              * @param {String} html
32490              */
32491             beforesync: true,
32492              /**
32493              * @event beforepush
32494              * Fires before the iframe editor is updated with content from the textarea. Return false
32495              * to cancel the push.
32496              * @param {HtmlEditor} this
32497              * @param {String} html
32498              */
32499             beforepush: true,
32500              /**
32501              * @event sync
32502              * Fires when the textarea is updated with content from the editor iframe.
32503              * @param {HtmlEditor} this
32504              * @param {String} html
32505              */
32506             sync: true,
32507              /**
32508              * @event push
32509              * Fires when the iframe editor is updated with content from the textarea.
32510              * @param {HtmlEditor} this
32511              * @param {String} html
32512              */
32513             push: true,
32514              /**
32515              * @event editmodechange
32516              * Fires when the editor switches edit modes
32517              * @param {HtmlEditor} this
32518              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32519              */
32520             editmodechange: true,
32521             /**
32522              * @event editorevent
32523              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32524              * @param {HtmlEditor} this
32525              */
32526             editorevent: true,
32527             /**
32528              * @event firstfocus
32529              * Fires when on first focus - needed by toolbars..
32530              * @param {HtmlEditor} this
32531              */
32532             firstfocus: true,
32533             /**
32534              * @event autosave
32535              * Auto save the htmlEditor value as a file into Events
32536              * @param {HtmlEditor} this
32537              */
32538             autosave: true,
32539             /**
32540              * @event savedpreview
32541              * preview the saved version of htmlEditor
32542              * @param {HtmlEditor} this
32543              */
32544             savedpreview: true
32545         });
32546 };
32547
32548
32549 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
32550     
32551     
32552       /**
32553      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
32554      */
32555     toolbars : false,
32556     
32557      /**
32558     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32559     */
32560     btns : [],
32561    
32562      /**
32563      * @cfg {String} resize  (none|both|horizontal|vertical) - css resize of element
32564      */
32565     resize : false,
32566      /**
32567      * @cfg {Number} height (in pixels)
32568      */   
32569     height: 300,
32570    /**
32571      * @cfg {Number} width (in pixels)
32572      */   
32573     width: false,
32574     
32575     /**
32576      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32577      * 
32578      */
32579     stylesheets: false,
32580     
32581     // id of frame..
32582     frameId: false,
32583     
32584     // private properties
32585     validationEvent : false,
32586     deferHeight: true,
32587     initialized : false,
32588     activated : false,
32589     
32590     onFocus : Roo.emptyFn,
32591     iframePad:3,
32592     hideMode:'offsets',
32593     
32594     tbContainer : false,
32595     
32596     bodyCls : '',
32597     
32598     toolbarContainer :function() {
32599         return this.wrap.select('.x-html-editor-tb',true).first();
32600     },
32601
32602     /**
32603      * Protected method that will not generally be called directly. It
32604      * is called when the editor creates its toolbar. Override this method if you need to
32605      * add custom toolbar buttons.
32606      * @param {HtmlEditor} editor
32607      */
32608     createToolbar : function(){
32609         Roo.log('renewing');
32610         Roo.log("create toolbars");
32611         
32612         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32613         this.toolbars[0].render(this.toolbarContainer());
32614         
32615         return;
32616         
32617 //        if (!editor.toolbars || !editor.toolbars.length) {
32618 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32619 //        }
32620 //        
32621 //        for (var i =0 ; i < editor.toolbars.length;i++) {
32622 //            editor.toolbars[i] = Roo.factory(
32623 //                    typeof(editor.toolbars[i]) == 'string' ?
32624 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
32625 //                Roo.bootstrap.form.HtmlEditor);
32626 //            editor.toolbars[i].init(editor);
32627 //        }
32628     },
32629
32630      
32631     // private
32632     onRender : function(ct, position)
32633     {
32634        // Roo.log("Call onRender: " + this.xtype);
32635         var _t = this;
32636         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32637       
32638         this.wrap = this.inputEl().wrap({
32639             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32640         });
32641         
32642         this.editorcore.onRender(ct, position);
32643          
32644          
32645         this.createToolbar(this);
32646        
32647         
32648           
32649         
32650     },
32651
32652     // private
32653     onResize : function(w, h)
32654     {
32655         Roo.log('resize: ' +w + ',' + h );
32656         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32657         var ew = false;
32658         var eh = false;
32659         
32660         if(this.inputEl() ){
32661             if(typeof w == 'number'){
32662                 var aw = w - this.wrap.getFrameWidth('lr');
32663                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32664                 ew = aw;
32665             }
32666             if(typeof h == 'number'){
32667                  var tbh = -11;  // fixme it needs to tool bar size!
32668                 for (var i =0; i < this.toolbars.length;i++) {
32669                     // fixme - ask toolbars for heights?
32670                     tbh += this.toolbars[i].el.getHeight();
32671                     //if (this.toolbars[i].footer) {
32672                     //    tbh += this.toolbars[i].footer.el.getHeight();
32673                     //}
32674                 }
32675               
32676                 
32677                 
32678                 
32679                 
32680                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32681                 ah -= 5; // knock a few pixes off for look..
32682                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32683                 var eh = ah;
32684             }
32685         }
32686         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32687         this.editorcore.onResize(ew,eh);
32688         
32689     },
32690
32691     /**
32692      * Toggles the editor between standard and source edit mode.
32693      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32694      */
32695     toggleSourceEdit : function(sourceEditMode)
32696     {
32697         this.editorcore.toggleSourceEdit(sourceEditMode);
32698         
32699         if(this.editorcore.sourceEditMode){
32700             Roo.log('editor - showing textarea');
32701             
32702 //            Roo.log('in');
32703 //            Roo.log(this.syncValue());
32704             this.syncValue();
32705             this.inputEl().removeClass(['hide', 'x-hidden']);
32706             this.inputEl().dom.removeAttribute('tabIndex');
32707             this.inputEl().focus();
32708         }else{
32709             Roo.log('editor - hiding textarea');
32710 //            Roo.log('out')
32711 //            Roo.log(this.pushValue()); 
32712             this.pushValue();
32713             
32714             this.inputEl().addClass(['hide', 'x-hidden']);
32715             this.inputEl().dom.setAttribute('tabIndex', -1);
32716             //this.deferFocus();
32717         }
32718          
32719         //if(this.resizable){
32720         //    this.setSize(this.wrap.getSize());
32721         //}
32722         
32723         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32724     },
32725  
32726     // private (for BoxComponent)
32727     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32728
32729     // private (for BoxComponent)
32730     getResizeEl : function(){
32731         return this.wrap;
32732     },
32733
32734     // private (for BoxComponent)
32735     getPositionEl : function(){
32736         return this.wrap;
32737     },
32738
32739     // private
32740     initEvents : function(){
32741         this.originalValue = this.getValue();
32742     },
32743
32744 //    /**
32745 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32746 //     * @method
32747 //     */
32748 //    markInvalid : Roo.emptyFn,
32749 //    /**
32750 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32751 //     * @method
32752 //     */
32753 //    clearInvalid : Roo.emptyFn,
32754
32755     setValue : function(v){
32756         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32757         this.editorcore.pushValue();
32758     },
32759
32760      
32761     // private
32762     deferFocus : function(){
32763         this.focus.defer(10, this);
32764     },
32765
32766     // doc'ed in Field
32767     focus : function(){
32768         this.editorcore.focus();
32769         
32770     },
32771       
32772
32773     // private
32774     onDestroy : function(){
32775         
32776         
32777         
32778         if(this.rendered){
32779             
32780             for (var i =0; i < this.toolbars.length;i++) {
32781                 // fixme - ask toolbars for heights?
32782                 this.toolbars[i].onDestroy();
32783             }
32784             
32785             this.wrap.dom.innerHTML = '';
32786             this.wrap.remove();
32787         }
32788     },
32789
32790     // private
32791     onFirstFocus : function(){
32792         //Roo.log("onFirstFocus");
32793         this.editorcore.onFirstFocus();
32794          for (var i =0; i < this.toolbars.length;i++) {
32795             this.toolbars[i].onFirstFocus();
32796         }
32797         
32798     },
32799     
32800     // private
32801     syncValue : function()
32802     {   
32803         this.editorcore.syncValue();
32804     },
32805     
32806     pushValue : function()
32807     {   
32808         this.editorcore.pushValue();
32809     }
32810      
32811     
32812     // hide stuff that is not compatible
32813     /**
32814      * @event blur
32815      * @hide
32816      */
32817     /**
32818      * @event change
32819      * @hide
32820      */
32821     /**
32822      * @event focus
32823      * @hide
32824      */
32825     /**
32826      * @event specialkey
32827      * @hide
32828      */
32829     /**
32830      * @cfg {String} fieldClass @hide
32831      */
32832     /**
32833      * @cfg {String} focusClass @hide
32834      */
32835     /**
32836      * @cfg {String} autoCreate @hide
32837      */
32838     /**
32839      * @cfg {String} inputType @hide
32840      */
32841      
32842     /**
32843      * @cfg {String} invalidText @hide
32844      */
32845     /**
32846      * @cfg {String} msgFx @hide
32847      */
32848     /**
32849      * @cfg {String} validateOnBlur @hide
32850      */
32851 });
32852  
32853     
32854    
32855    
32856    
32857       
32858 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32859 /**
32860  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32861  * @parent Roo.bootstrap.form.HtmlEditor
32862  * @extends Roo.bootstrap.nav.Simplebar
32863  * Basic Toolbar
32864  * 
32865  * @example
32866  * Usage:
32867  *
32868  new Roo.bootstrap.form.HtmlEditor({
32869     ....
32870     toolbars : [
32871         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32872             disable : { fonts: 1 , format: 1, ..., ... , ...],
32873             btns : [ .... ]
32874         })
32875     }
32876      
32877  * 
32878  * @cfg {Object} disable List of elements to disable..
32879  * @cfg {Array} btns List of additional buttons.
32880  * 
32881  * 
32882  * NEEDS Extra CSS? 
32883  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32884  */
32885  
32886 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32887 {
32888     
32889     Roo.apply(this, config);
32890     
32891     // default disabled, based on 'good practice'..
32892     this.disable = this.disable || {};
32893     Roo.applyIf(this.disable, {
32894         fontSize : true,
32895         colors : true,
32896         specialElements : true
32897     });
32898     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32899     
32900     this.editor = config.editor;
32901     this.editorcore = config.editor.editorcore;
32902     
32903     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32904     
32905     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32906     // dont call parent... till later.
32907 }
32908 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
32909      
32910     bar : true,
32911     
32912     editor : false,
32913     editorcore : false,
32914     
32915     
32916     formats : [
32917         "p" ,  
32918         "h1","h2","h3","h4","h5","h6", 
32919         "pre", "code", 
32920         "abbr", "acronym", "address", "cite", "samp", "var",
32921         'div','span'
32922     ],
32923     
32924     onRender : function(ct, position)
32925     {
32926        // Roo.log("Call onRender: " + this.xtype);
32927         
32928        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32929        Roo.log(this.el);
32930        this.el.dom.style.marginBottom = '0';
32931        var _this = this;
32932        var editorcore = this.editorcore;
32933        var editor= this.editor;
32934        
32935        var children = [];
32936        var btn = function(id,cmd , toggle, handler, html){
32937        
32938             var  event = toggle ? 'toggle' : 'click';
32939        
32940             var a = {
32941                 size : 'sm',
32942                 xtype: 'Button',
32943                 xns: Roo.bootstrap,
32944                 //glyphicon : id,
32945                 fa: id,
32946                 cmd : id || cmd,
32947                 enableToggle:toggle !== false,
32948                 html : html || '',
32949                 pressed : toggle ? false : null,
32950                 listeners : {}
32951             };
32952             a.listeners[toggle ? 'toggle' : 'click'] = function() {
32953                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
32954             };
32955             children.push(a);
32956             return a;
32957        }
32958        
32959     //    var cb_box = function...
32960         
32961         var style = {
32962                 xtype: 'Button',
32963                 size : 'sm',
32964                 xns: Roo.bootstrap,
32965                 fa : 'font',
32966                 //html : 'submit'
32967                 menu : {
32968                     xtype: 'Menu',
32969                     xns: Roo.bootstrap,
32970                     items:  []
32971                 }
32972         };
32973         Roo.each(this.formats, function(f) {
32974             style.menu.items.push({
32975                 xtype :'MenuItem',
32976                 xns: Roo.bootstrap,
32977                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32978                 tagname : f,
32979                 listeners : {
32980                     click : function()
32981                     {
32982                         editorcore.insertTag(this.tagname);
32983                         editor.focus();
32984                     }
32985                 }
32986                 
32987             });
32988         });
32989         children.push(style);   
32990         
32991         btn('bold',false,true);
32992         btn('italic',false,true);
32993         btn('align-left', 'justifyleft',true);
32994         btn('align-center', 'justifycenter',true);
32995         btn('align-right' , 'justifyright',true);
32996         btn('link', false, false, function(btn) {
32997             //Roo.log("create link?");
32998             var url = prompt(this.createLinkText, this.defaultLinkValue);
32999             if(url && url != 'http:/'+'/'){
33000                 this.editorcore.relayCmd('createlink', url);
33001             }
33002         }),
33003         btn('list','insertunorderedlist',true);
33004         btn('list-ol','insertorderedlist',true);
33005
33006         btn('pencil', false,true, function(btn){
33007                 Roo.log(this);
33008                 this.toggleSourceEdit(btn.pressed);
33009         });
33010         
33011         if (this.editor.btns.length > 0) {
33012             for (var i = 0; i<this.editor.btns.length; i++) {
33013                 children.push(this.editor.btns[i]);
33014             }
33015         }
33016         
33017         /*
33018         var cog = {
33019                 xtype: 'Button',
33020                 size : 'sm',
33021                 xns: Roo.bootstrap,
33022                 glyphicon : 'cog',
33023                 //html : 'submit'
33024                 menu : {
33025                     xtype: 'Menu',
33026                     xns: Roo.bootstrap,
33027                     items:  []
33028                 }
33029         };
33030         
33031         cog.menu.items.push({
33032             xtype :'MenuItem',
33033             xns: Roo.bootstrap,
33034             html : Clean styles,
33035             tagname : f,
33036             listeners : {
33037                 click : function()
33038                 {
33039                     editorcore.insertTag(this.tagname);
33040                     editor.focus();
33041                 }
33042             }
33043             
33044         });
33045        */
33046         
33047          
33048        this.xtype = 'NavSimplebar';
33049         
33050         for(var i=0;i< children.length;i++) {
33051             
33052             this.buttons.add(this.addxtypeChild(children[i]));
33053             
33054         }
33055         
33056         editor.on('editorevent', this.updateToolbar, this);
33057     },
33058     onBtnClick : function(id)
33059     {
33060        this.editorcore.relayCmd(id);
33061        this.editorcore.focus();
33062     },
33063     
33064     /**
33065      * Protected method that will not generally be called directly. It triggers
33066      * a toolbar update by reading the markup state of the current selection in the editor.
33067      */
33068     updateToolbar: function(){
33069
33070         if(!this.editorcore.activated){
33071             this.editor.onFirstFocus(); // is this neeed?
33072             return;
33073         }
33074
33075         var btns = this.buttons; 
33076         var doc = this.editorcore.doc;
33077         btns.get('bold').setActive(doc.queryCommandState('bold'));
33078         btns.get('italic').setActive(doc.queryCommandState('italic'));
33079         //btns.get('underline').setActive(doc.queryCommandState('underline'));
33080         
33081         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
33082         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
33083         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
33084         
33085         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
33086         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
33087          /*
33088         
33089         var ans = this.editorcore.getAllAncestors();
33090         if (this.formatCombo) {
33091             
33092             
33093             var store = this.formatCombo.store;
33094             this.formatCombo.setValue("");
33095             for (var i =0; i < ans.length;i++) {
33096                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
33097                     // select it..
33098                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
33099                     break;
33100                 }
33101             }
33102         }
33103         
33104         
33105         
33106         // hides menus... - so this cant be on a menu...
33107         Roo.bootstrap.MenuMgr.hideAll();
33108         */
33109         Roo.bootstrap.menu.Manager.hideAll();
33110         //this.editorsyncValue();
33111     },
33112     onFirstFocus: function() {
33113         this.buttons.each(function(item){
33114            item.enable();
33115         });
33116     },
33117     toggleSourceEdit : function(sourceEditMode){
33118         
33119           
33120         if(sourceEditMode){
33121             Roo.log("disabling buttons");
33122            this.buttons.each( function(item){
33123                 if(item.cmd != 'pencil'){
33124                     item.disable();
33125                 }
33126             });
33127           
33128         }else{
33129             Roo.log("enabling buttons");
33130             if(this.editorcore.initialized){
33131                 this.buttons.each( function(item){
33132                     item.enable();
33133                 });
33134             }
33135             
33136         }
33137         Roo.log("calling toggole on editor");
33138         // tell the editor that it's been pressed..
33139         this.editor.toggleSourceEdit(sourceEditMode);
33140        
33141     }
33142 });
33143
33144
33145
33146
33147  
33148 /*
33149  * - LGPL
33150  */
33151
33152 /**
33153  * @class Roo.bootstrap.form.Markdown
33154  * @extends Roo.bootstrap.form.TextArea
33155  * Bootstrap Showdown editable area
33156  * @cfg {string} content
33157  * 
33158  * @constructor
33159  * Create a new Showdown
33160  */
33161
33162 Roo.bootstrap.form.Markdown = function(config){
33163     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33164    
33165 };
33166
33167 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
33168     
33169     editing :false,
33170     
33171     initEvents : function()
33172     {
33173         
33174         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33175         this.markdownEl = this.el.createChild({
33176             cls : 'roo-markdown-area'
33177         });
33178         this.inputEl().addClass('d-none');
33179         if (this.getValue() == '') {
33180             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33181             
33182         } else {
33183             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33184         }
33185         this.markdownEl.on('click', this.toggleTextEdit, this);
33186         this.on('blur', this.toggleTextEdit, this);
33187         this.on('specialkey', this.resizeTextArea, this);
33188     },
33189     
33190     toggleTextEdit : function()
33191     {
33192         var sh = this.markdownEl.getHeight();
33193         this.inputEl().addClass('d-none');
33194         this.markdownEl.addClass('d-none');
33195         if (!this.editing) {
33196             // show editor?
33197             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33198             this.inputEl().removeClass('d-none');
33199             this.inputEl().focus();
33200             this.editing = true;
33201             return;
33202         }
33203         // show showdown...
33204         this.updateMarkdown();
33205         this.markdownEl.removeClass('d-none');
33206         this.editing = false;
33207         return;
33208     },
33209     updateMarkdown : function()
33210     {
33211         if (this.getValue() == '') {
33212             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33213             return;
33214         }
33215  
33216         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33217     },
33218     
33219     resizeTextArea: function () {
33220         
33221         var sh = 100;
33222         Roo.log([sh, this.getValue().split("\n").length * 30]);
33223         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33224     },
33225     setValue : function(val)
33226     {
33227         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33228         if (!this.editing) {
33229             this.updateMarkdown();
33230         }
33231         
33232     },
33233     focus : function()
33234     {
33235         if (!this.editing) {
33236             this.toggleTextEdit();
33237         }
33238         
33239     }
33240
33241
33242 });/*
33243  * Based on:
33244  * Ext JS Library 1.1.1
33245  * Copyright(c) 2006-2007, Ext JS, LLC.
33246  *
33247  * Originally Released Under LGPL - original licence link has changed is not relivant.
33248  *
33249  * Fork - LGPL
33250  * <script type="text/javascript">
33251  */
33252  
33253 /**
33254  * @class Roo.bootstrap.PagingToolbar
33255  * @extends Roo.bootstrap.nav.Simplebar
33256  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33257  * @constructor
33258  * Create a new PagingToolbar
33259  * @param {Object} config The config object
33260  * @param {Roo.data.Store} store
33261  */
33262 Roo.bootstrap.PagingToolbar = function(config)
33263 {
33264     // old args format still supported... - xtype is prefered..
33265         // created from xtype...
33266     
33267     this.ds = config.dataSource;
33268     
33269     if (config.store && !this.ds) {
33270         this.store= Roo.factory(config.store, Roo.data);
33271         this.ds = this.store;
33272         this.ds.xmodule = this.xmodule || false;
33273     }
33274     
33275     this.toolbarItems = [];
33276     if (config.items) {
33277         this.toolbarItems = config.items;
33278     }
33279     
33280     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33281     
33282     this.cursor = 0;
33283     
33284     if (this.ds) { 
33285         this.bind(this.ds);
33286     }
33287     
33288     if (Roo.bootstrap.version == 4) {
33289         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33290     } else {
33291         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33292     }
33293     
33294 };
33295
33296 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33297     /**
33298      * @cfg {Roo.bootstrap.Button} buttons[]
33299      * Buttons for the toolbar
33300      */
33301      /**
33302      * @cfg {Roo.data.Store} store
33303      * The underlying data store providing the paged data
33304      */
33305     /**
33306      * @cfg {String/HTMLElement/Element} container
33307      * container The id or element that will contain the toolbar
33308      */
33309     /**
33310      * @cfg {Boolean} displayInfo
33311      * True to display the displayMsg (defaults to false)
33312      */
33313     /**
33314      * @cfg {Number} pageSize
33315      * The number of records to display per page (defaults to 20)
33316      */
33317     pageSize: 20,
33318     /**
33319      * @cfg {String} displayMsg
33320      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33321      */
33322     displayMsg : 'Displaying {0} - {1} of {2}',
33323     /**
33324      * @cfg {String} emptyMsg
33325      * The message to display when no records are found (defaults to "No data to display")
33326      */
33327     emptyMsg : 'No data to display',
33328     /**
33329      * Customizable piece of the default paging text (defaults to "Page")
33330      * @type String
33331      */
33332     beforePageText : "Page",
33333     /**
33334      * Customizable piece of the default paging text (defaults to "of %0")
33335      * @type String
33336      */
33337     afterPageText : "of {0}",
33338     /**
33339      * Customizable piece of the default paging text (defaults to "First Page")
33340      * @type String
33341      */
33342     firstText : "First Page",
33343     /**
33344      * Customizable piece of the default paging text (defaults to "Previous Page")
33345      * @type String
33346      */
33347     prevText : "Previous Page",
33348     /**
33349      * Customizable piece of the default paging text (defaults to "Next Page")
33350      * @type String
33351      */
33352     nextText : "Next Page",
33353     /**
33354      * Customizable piece of the default paging text (defaults to "Last Page")
33355      * @type String
33356      */
33357     lastText : "Last Page",
33358     /**
33359      * Customizable piece of the default paging text (defaults to "Refresh")
33360      * @type String
33361      */
33362     refreshText : "Refresh",
33363
33364     buttons : false,
33365     // private
33366     onRender : function(ct, position) 
33367     {
33368         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33369         this.navgroup.parentId = this.id;
33370         this.navgroup.onRender(this.el, null);
33371         // add the buttons to the navgroup
33372         
33373         if(this.displayInfo){
33374             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33375             this.displayEl = this.el.select('.x-paging-info', true).first();
33376 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33377 //            this.displayEl = navel.el.select('span',true).first();
33378         }
33379         
33380         var _this = this;
33381         
33382         if(this.buttons){
33383             Roo.each(_this.buttons, function(e){ // this might need to use render????
33384                Roo.factory(e).render(_this.el);
33385             });
33386         }
33387             
33388         Roo.each(_this.toolbarItems, function(e) {
33389             _this.navgroup.addItem(e);
33390         });
33391         
33392         
33393         this.first = this.navgroup.addItem({
33394             tooltip: this.firstText,
33395             cls: "prev btn-outline-secondary",
33396             html : ' <i class="fa fa-step-backward"></i>',
33397             disabled: true,
33398             preventDefault: true,
33399             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33400         });
33401         
33402         this.prev =  this.navgroup.addItem({
33403             tooltip: this.prevText,
33404             cls: "prev btn-outline-secondary",
33405             html : ' <i class="fa fa-backward"></i>',
33406             disabled: true,
33407             preventDefault: true,
33408             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
33409         });
33410     //this.addSeparator();
33411         
33412         
33413         var field = this.navgroup.addItem( {
33414             tagtype : 'span',
33415             cls : 'x-paging-position  btn-outline-secondary',
33416              disabled: true,
33417             html : this.beforePageText  +
33418                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33419                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
33420          } ); //?? escaped?
33421         
33422         this.field = field.el.select('input', true).first();
33423         this.field.on("keydown", this.onPagingKeydown, this);
33424         this.field.on("focus", function(){this.dom.select();});
33425     
33426     
33427         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
33428         //this.field.setHeight(18);
33429         //this.addSeparator();
33430         this.next = this.navgroup.addItem({
33431             tooltip: this.nextText,
33432             cls: "next btn-outline-secondary",
33433             html : ' <i class="fa fa-forward"></i>',
33434             disabled: true,
33435             preventDefault: true,
33436             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
33437         });
33438         this.last = this.navgroup.addItem({
33439             tooltip: this.lastText,
33440             html : ' <i class="fa fa-step-forward"></i>',
33441             cls: "next btn-outline-secondary",
33442             disabled: true,
33443             preventDefault: true,
33444             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
33445         });
33446     //this.addSeparator();
33447         this.loading = this.navgroup.addItem({
33448             tooltip: this.refreshText,
33449             cls: "btn-outline-secondary",
33450             html : ' <i class="fa fa-refresh"></i>',
33451             preventDefault: true,
33452             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33453         });
33454         
33455     },
33456
33457     // private
33458     updateInfo : function(){
33459         if(this.displayEl){
33460             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33461             var msg = count == 0 ?
33462                 this.emptyMsg :
33463                 String.format(
33464                     this.displayMsg,
33465                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
33466                 );
33467             this.displayEl.update(msg);
33468         }
33469     },
33470
33471     // private
33472     onLoad : function(ds, r, o)
33473     {
33474         this.cursor = o.params && o.params.start ? o.params.start : 0;
33475         
33476         var d = this.getPageData(),
33477             ap = d.activePage,
33478             ps = d.pages;
33479         
33480         
33481         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33482         this.field.dom.value = ap;
33483         this.first.setDisabled(ap == 1);
33484         this.prev.setDisabled(ap == 1);
33485         this.next.setDisabled(ap == ps);
33486         this.last.setDisabled(ap == ps);
33487         this.loading.enable();
33488         this.updateInfo();
33489     },
33490
33491     // private
33492     getPageData : function(){
33493         var total = this.ds.getTotalCount();
33494         return {
33495             total : total,
33496             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33497             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33498         };
33499     },
33500
33501     // private
33502     onLoadError : function(proxy, o){
33503         this.loading.enable();
33504         if (this.ds.events.loadexception.listeners.length  < 2) {
33505             // nothing has been assigned to loadexception except this...
33506             // so 
33507             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33508
33509         }
33510     },
33511
33512     // private
33513     onPagingKeydown : function(e){
33514         var k = e.getKey();
33515         var d = this.getPageData();
33516         if(k == e.RETURN){
33517             var v = this.field.dom.value, pageNum;
33518             if(!v || isNaN(pageNum = parseInt(v, 10))){
33519                 this.field.dom.value = d.activePage;
33520                 return;
33521             }
33522             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33523             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33524             e.stopEvent();
33525         }
33526         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))
33527         {
33528           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33529           this.field.dom.value = pageNum;
33530           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33531           e.stopEvent();
33532         }
33533         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33534         {
33535           var v = this.field.dom.value, pageNum; 
33536           var increment = (e.shiftKey) ? 10 : 1;
33537           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33538                 increment *= -1;
33539           }
33540           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33541             this.field.dom.value = d.activePage;
33542             return;
33543           }
33544           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33545           {
33546             this.field.dom.value = parseInt(v, 10) + increment;
33547             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33548             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33549           }
33550           e.stopEvent();
33551         }
33552     },
33553
33554     // private
33555     beforeLoad : function(){
33556         if(this.loading){
33557             this.loading.disable();
33558         }
33559     },
33560
33561     // private
33562     onClick : function(which){
33563         
33564         var ds = this.ds;
33565         if (!ds) {
33566             return;
33567         }
33568         
33569         switch(which){
33570             case "first":
33571                 ds.load({params:{start: 0, limit: this.pageSize}});
33572             break;
33573             case "prev":
33574                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33575             break;
33576             case "next":
33577                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33578             break;
33579             case "last":
33580                 var total = ds.getTotalCount();
33581                 var extra = total % this.pageSize;
33582                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33583                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33584             break;
33585             case "refresh":
33586                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33587             break;
33588         }
33589     },
33590
33591     /**
33592      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33593      * @param {Roo.data.Store} store The data store to unbind
33594      */
33595     unbind : function(ds){
33596         ds.un("beforeload", this.beforeLoad, this);
33597         ds.un("load", this.onLoad, this);
33598         ds.un("loadexception", this.onLoadError, this);
33599         ds.un("remove", this.updateInfo, this);
33600         ds.un("add", this.updateInfo, this);
33601         this.ds = undefined;
33602     },
33603
33604     /**
33605      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33606      * @param {Roo.data.Store} store The data store to bind
33607      */
33608     bind : function(ds){
33609         ds.on("beforeload", this.beforeLoad, this);
33610         ds.on("load", this.onLoad, this);
33611         ds.on("loadexception", this.onLoadError, this);
33612         ds.on("remove", this.updateInfo, this);
33613         ds.on("add", this.updateInfo, this);
33614         this.ds = ds;
33615     }
33616 });/*
33617  * - LGPL
33618  *
33619  * element
33620  * 
33621  */
33622
33623 /**
33624  * @class Roo.bootstrap.MessageBar
33625  * @extends Roo.bootstrap.Component
33626  * Bootstrap MessageBar class
33627  * @cfg {String} html contents of the MessageBar
33628  * @cfg {String} weight (info | success | warning | danger) default info
33629  * @cfg {String} beforeClass insert the bar before the given class
33630  * @cfg {Boolean} closable (true | false) default false
33631  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33632  * 
33633  * @constructor
33634  * Create a new Element
33635  * @param {Object} config The config object
33636  */
33637
33638 Roo.bootstrap.MessageBar = function(config){
33639     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33640 };
33641
33642 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33643     
33644     html: '',
33645     weight: 'info',
33646     closable: false,
33647     fixed: false,
33648     beforeClass: 'bootstrap-sticky-wrap',
33649     
33650     getAutoCreate : function(){
33651         
33652         var cfg = {
33653             tag: 'div',
33654             cls: 'alert alert-dismissable alert-' + this.weight,
33655             cn: [
33656                 {
33657                     tag: 'span',
33658                     cls: 'message',
33659                     html: this.html || ''
33660                 }
33661             ]
33662         };
33663         
33664         if(this.fixed){
33665             cfg.cls += ' alert-messages-fixed';
33666         }
33667         
33668         if(this.closable){
33669             cfg.cn.push({
33670                 tag: 'button',
33671                 cls: 'close',
33672                 html: 'x'
33673             });
33674         }
33675         
33676         return cfg;
33677     },
33678     
33679     onRender : function(ct, position)
33680     {
33681         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33682         
33683         if(!this.el){
33684             var cfg = Roo.apply({},  this.getAutoCreate());
33685             cfg.id = Roo.id();
33686             
33687             if (this.cls) {
33688                 cfg.cls += ' ' + this.cls;
33689             }
33690             if (this.style) {
33691                 cfg.style = this.style;
33692             }
33693             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33694             
33695             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33696         }
33697         
33698         this.el.select('>button.close').on('click', this.hide, this);
33699         
33700     },
33701     
33702     show : function()
33703     {
33704         if (!this.rendered) {
33705             this.render();
33706         }
33707         
33708         this.el.show();
33709         
33710         this.fireEvent('show', this);
33711         
33712     },
33713     
33714     hide : function()
33715     {
33716         if (!this.rendered) {
33717             this.render();
33718         }
33719         
33720         this.el.hide();
33721         
33722         this.fireEvent('hide', this);
33723     },
33724     
33725     update : function()
33726     {
33727 //        var e = this.el.dom.firstChild;
33728 //        
33729 //        if(this.closable){
33730 //            e = e.nextSibling;
33731 //        }
33732 //        
33733 //        e.data = this.html || '';
33734
33735         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33736     }
33737    
33738 });
33739
33740  
33741
33742      /*
33743  * - LGPL
33744  *
33745  * Graph
33746  * 
33747  */
33748
33749
33750 /**
33751  * @class Roo.bootstrap.Graph
33752  * @extends Roo.bootstrap.Component
33753  * Bootstrap Graph class
33754 > Prameters
33755  -sm {number} sm 4
33756  -md {number} md 5
33757  @cfg {String} graphtype  bar | vbar | pie
33758  @cfg {number} g_x coodinator | centre x (pie)
33759  @cfg {number} g_y coodinator | centre y (pie)
33760  @cfg {number} g_r radius (pie)
33761  @cfg {number} g_height height of the chart (respected by all elements in the set)
33762  @cfg {number} g_width width of the chart (respected by all elements in the set)
33763  @cfg {Object} title The title of the chart
33764     
33765  -{Array}  values
33766  -opts (object) options for the chart 
33767      o {
33768      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33769      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33770      o vgutter (number)
33771      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.
33772      o stacked (boolean) whether or not to tread values as in a stacked bar chart
33773      o to
33774      o stretch (boolean)
33775      o }
33776  -opts (object) options for the pie
33777      o{
33778      o cut
33779      o startAngle (number)
33780      o endAngle (number)
33781      } 
33782  *
33783  * @constructor
33784  * Create a new Input
33785  * @param {Object} config The config object
33786  */
33787
33788 Roo.bootstrap.Graph = function(config){
33789     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33790     
33791     this.addEvents({
33792         // img events
33793         /**
33794          * @event click
33795          * The img click event for the img.
33796          * @param {Roo.EventObject} e
33797          */
33798         "click" : true
33799     });
33800 };
33801
33802 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
33803     
33804     sm: 4,
33805     md: 5,
33806     graphtype: 'bar',
33807     g_height: 250,
33808     g_width: 400,
33809     g_x: 50,
33810     g_y: 50,
33811     g_r: 30,
33812     opts:{
33813         //g_colors: this.colors,
33814         g_type: 'soft',
33815         g_gutter: '20%'
33816
33817     },
33818     title : false,
33819
33820     getAutoCreate : function(){
33821         
33822         var cfg = {
33823             tag: 'div',
33824             html : null
33825         };
33826         
33827         
33828         return  cfg;
33829     },
33830
33831     onRender : function(ct,position){
33832         
33833         
33834         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33835         
33836         if (typeof(Raphael) == 'undefined') {
33837             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33838             return;
33839         }
33840         
33841         this.raphael = Raphael(this.el.dom);
33842         
33843                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33844                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33845                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33846                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33847                 /*
33848                 r.text(160, 10, "Single Series Chart").attr(txtattr);
33849                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33850                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33851                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33852                 
33853                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33854                 r.barchart(330, 10, 300, 220, data1);
33855                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33856                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33857                 */
33858                 
33859                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33860                 // r.barchart(30, 30, 560, 250,  xdata, {
33861                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33862                 //     axis : "0 0 1 1",
33863                 //     axisxlabels :  xdata
33864                 //     //yvalues : cols,
33865                    
33866                 // });
33867 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33868 //        
33869 //        this.load(null,xdata,{
33870 //                axis : "0 0 1 1",
33871 //                axisxlabels :  xdata
33872 //                });
33873
33874     },
33875
33876     load : function(graphtype,xdata,opts)
33877     {
33878         this.raphael.clear();
33879         if(!graphtype) {
33880             graphtype = this.graphtype;
33881         }
33882         if(!opts){
33883             opts = this.opts;
33884         }
33885         var r = this.raphael,
33886             fin = function () {
33887                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33888             },
33889             fout = function () {
33890                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33891             },
33892             pfin = function() {
33893                 this.sector.stop();
33894                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33895
33896                 if (this.label) {
33897                     this.label[0].stop();
33898                     this.label[0].attr({ r: 7.5 });
33899                     this.label[1].attr({ "font-weight": 800 });
33900                 }
33901             },
33902             pfout = function() {
33903                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33904
33905                 if (this.label) {
33906                     this.label[0].animate({ r: 5 }, 500, "bounce");
33907                     this.label[1].attr({ "font-weight": 400 });
33908                 }
33909             };
33910
33911         switch(graphtype){
33912             case 'bar':
33913                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33914                 break;
33915             case 'hbar':
33916                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33917                 break;
33918             case 'pie':
33919 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
33920 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33921 //            
33922                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33923                 
33924                 break;
33925
33926         }
33927         
33928         if(this.title){
33929             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33930         }
33931         
33932     },
33933     
33934     setTitle: function(o)
33935     {
33936         this.title = o;
33937     },
33938     
33939     initEvents: function() {
33940         
33941         if(!this.href){
33942             this.el.on('click', this.onClick, this);
33943         }
33944     },
33945     
33946     onClick : function(e)
33947     {
33948         Roo.log('img onclick');
33949         this.fireEvent('click', this, e);
33950     }
33951    
33952 });
33953
33954  
33955 Roo.bootstrap.dash = {};/*
33956  * - LGPL
33957  *
33958  * numberBox
33959  * 
33960  */
33961 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33962
33963 /**
33964  * @class Roo.bootstrap.dash.NumberBox
33965  * @extends Roo.bootstrap.Component
33966  * Bootstrap NumberBox class
33967  * @cfg {String} headline Box headline
33968  * @cfg {String} content Box content
33969  * @cfg {String} icon Box icon
33970  * @cfg {String} footer Footer text
33971  * @cfg {String} fhref Footer href
33972  * 
33973  * @constructor
33974  * Create a new NumberBox
33975  * @param {Object} config The config object
33976  */
33977
33978
33979 Roo.bootstrap.dash.NumberBox = function(config){
33980     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33981     
33982 };
33983
33984 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
33985     
33986     headline : '',
33987     content : '',
33988     icon : '',
33989     footer : '',
33990     fhref : '',
33991     ficon : '',
33992     
33993     getAutoCreate : function(){
33994         
33995         var cfg = {
33996             tag : 'div',
33997             cls : 'small-box ',
33998             cn : [
33999                 {
34000                     tag : 'div',
34001                     cls : 'inner',
34002                     cn :[
34003                         {
34004                             tag : 'h3',
34005                             cls : 'roo-headline',
34006                             html : this.headline
34007                         },
34008                         {
34009                             tag : 'p',
34010                             cls : 'roo-content',
34011                             html : this.content
34012                         }
34013                     ]
34014                 }
34015             ]
34016         };
34017         
34018         if(this.icon){
34019             cfg.cn.push({
34020                 tag : 'div',
34021                 cls : 'icon',
34022                 cn :[
34023                     {
34024                         tag : 'i',
34025                         cls : 'ion ' + this.icon
34026                     }
34027                 ]
34028             });
34029         }
34030         
34031         if(this.footer){
34032             var footer = {
34033                 tag : 'a',
34034                 cls : 'small-box-footer',
34035                 href : this.fhref || '#',
34036                 html : this.footer
34037             };
34038             
34039             cfg.cn.push(footer);
34040             
34041         }
34042         
34043         return  cfg;
34044     },
34045
34046     onRender : function(ct,position){
34047         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
34048
34049
34050        
34051                 
34052     },
34053
34054     setHeadline: function (value)
34055     {
34056         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34057     },
34058     
34059     setFooter: function (value, href)
34060     {
34061         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34062         
34063         if(href){
34064             this.el.select('a.small-box-footer',true).first().attr('href', href);
34065         }
34066         
34067     },
34068
34069     setContent: function (value)
34070     {
34071         this.el.select('.roo-content',true).first().dom.innerHTML = value;
34072     },
34073
34074     initEvents: function() 
34075     {   
34076         
34077     }
34078     
34079 });
34080
34081  
34082 /*
34083  * - LGPL
34084  *
34085  * TabBox
34086  * 
34087  */
34088 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34089
34090 /**
34091  * @class Roo.bootstrap.dash.TabBox
34092  * @extends Roo.bootstrap.Component
34093  * @children Roo.bootstrap.dash.TabPane
34094  * Bootstrap TabBox class
34095  * @cfg {String} title Title of the TabBox
34096  * @cfg {String} icon Icon of the TabBox
34097  * @cfg {Boolean} showtabs (true|false) show the tabs default true
34098  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34099  * 
34100  * @constructor
34101  * Create a new TabBox
34102  * @param {Object} config The config object
34103  */
34104
34105
34106 Roo.bootstrap.dash.TabBox = function(config){
34107     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34108     this.addEvents({
34109         // raw events
34110         /**
34111          * @event addpane
34112          * When a pane is added
34113          * @param {Roo.bootstrap.dash.TabPane} pane
34114          */
34115         "addpane" : true,
34116         /**
34117          * @event activatepane
34118          * When a pane is activated
34119          * @param {Roo.bootstrap.dash.TabPane} pane
34120          */
34121         "activatepane" : true
34122         
34123          
34124     });
34125     
34126     this.panes = [];
34127 };
34128
34129 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
34130
34131     title : '',
34132     icon : false,
34133     showtabs : true,
34134     tabScrollable : false,
34135     
34136     getChildContainer : function()
34137     {
34138         return this.el.select('.tab-content', true).first();
34139     },
34140     
34141     getAutoCreate : function(){
34142         
34143         var header = {
34144             tag: 'li',
34145             cls: 'pull-left header',
34146             html: this.title,
34147             cn : []
34148         };
34149         
34150         if(this.icon){
34151             header.cn.push({
34152                 tag: 'i',
34153                 cls: 'fa ' + this.icon
34154             });
34155         }
34156         
34157         var h = {
34158             tag: 'ul',
34159             cls: 'nav nav-tabs pull-right',
34160             cn: [
34161                 header
34162             ]
34163         };
34164         
34165         if(this.tabScrollable){
34166             h = {
34167                 tag: 'div',
34168                 cls: 'tab-header',
34169                 cn: [
34170                     {
34171                         tag: 'ul',
34172                         cls: 'nav nav-tabs pull-right',
34173                         cn: [
34174                             header
34175                         ]
34176                     }
34177                 ]
34178             };
34179         }
34180         
34181         var cfg = {
34182             tag: 'div',
34183             cls: 'nav-tabs-custom',
34184             cn: [
34185                 h,
34186                 {
34187                     tag: 'div',
34188                     cls: 'tab-content no-padding',
34189                     cn: []
34190                 }
34191             ]
34192         };
34193
34194         return  cfg;
34195     },
34196     initEvents : function()
34197     {
34198         //Roo.log('add add pane handler');
34199         this.on('addpane', this.onAddPane, this);
34200     },
34201      /**
34202      * Updates the box title
34203      * @param {String} html to set the title to.
34204      */
34205     setTitle : function(value)
34206     {
34207         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34208     },
34209     onAddPane : function(pane)
34210     {
34211         this.panes.push(pane);
34212         //Roo.log('addpane');
34213         //Roo.log(pane);
34214         // tabs are rendere left to right..
34215         if(!this.showtabs){
34216             return;
34217         }
34218         
34219         var ctr = this.el.select('.nav-tabs', true).first();
34220          
34221          
34222         var existing = ctr.select('.nav-tab',true);
34223         var qty = existing.getCount();;
34224         
34225         
34226         var tab = ctr.createChild({
34227             tag : 'li',
34228             cls : 'nav-tab' + (qty ? '' : ' active'),
34229             cn : [
34230                 {
34231                     tag : 'a',
34232                     href:'#',
34233                     html : pane.title
34234                 }
34235             ]
34236         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34237         pane.tab = tab;
34238         
34239         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34240         if (!qty) {
34241             pane.el.addClass('active');
34242         }
34243         
34244                 
34245     },
34246     onTabClick : function(ev,un,ob,pane)
34247     {
34248         //Roo.log('tab - prev default');
34249         ev.preventDefault();
34250         
34251         
34252         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34253         pane.tab.addClass('active');
34254         //Roo.log(pane.title);
34255         this.getChildContainer().select('.tab-pane',true).removeClass('active');
34256         // technically we should have a deactivate event.. but maybe add later.
34257         // and it should not de-activate the selected tab...
34258         this.fireEvent('activatepane', pane);
34259         pane.el.addClass('active');
34260         pane.fireEvent('activate');
34261         
34262         
34263     },
34264     
34265     getActivePane : function()
34266     {
34267         var r = false;
34268         Roo.each(this.panes, function(p) {
34269             if(p.el.hasClass('active')){
34270                 r = p;
34271                 return false;
34272             }
34273             
34274             return;
34275         });
34276         
34277         return r;
34278     }
34279     
34280     
34281 });
34282
34283  
34284 /*
34285  * - LGPL
34286  *
34287  * Tab pane
34288  * 
34289  */
34290 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34291 /**
34292  * @class Roo.bootstrap.TabPane
34293  * @extends Roo.bootstrap.Component
34294  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
34295  * Bootstrap TabPane class
34296  * @cfg {Boolean} active (false | true) Default false
34297  * @cfg {String} title title of panel
34298
34299  * 
34300  * @constructor
34301  * Create a new TabPane
34302  * @param {Object} config The config object
34303  */
34304
34305 Roo.bootstrap.dash.TabPane = function(config){
34306     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34307     
34308     this.addEvents({
34309         // raw events
34310         /**
34311          * @event activate
34312          * When a pane is activated
34313          * @param {Roo.bootstrap.dash.TabPane} pane
34314          */
34315         "activate" : true
34316          
34317     });
34318 };
34319
34320 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
34321     
34322     active : false,
34323     title : '',
34324     
34325     // the tabBox that this is attached to.
34326     tab : false,
34327      
34328     getAutoCreate : function() 
34329     {
34330         var cfg = {
34331             tag: 'div',
34332             cls: 'tab-pane'
34333         };
34334         
34335         if(this.active){
34336             cfg.cls += ' active';
34337         }
34338         
34339         return cfg;
34340     },
34341     initEvents  : function()
34342     {
34343         //Roo.log('trigger add pane handler');
34344         this.parent().fireEvent('addpane', this)
34345     },
34346     
34347      /**
34348      * Updates the tab title 
34349      * @param {String} html to set the title to.
34350      */
34351     setTitle: function(str)
34352     {
34353         if (!this.tab) {
34354             return;
34355         }
34356         this.title = str;
34357         this.tab.select('a', true).first().dom.innerHTML = str;
34358         
34359     }
34360     
34361     
34362     
34363 });
34364
34365  
34366
34367
34368  /*
34369  * - LGPL
34370  *
34371  * Tooltip
34372  * 
34373  */
34374
34375 /**
34376  * @class Roo.bootstrap.Tooltip
34377  * Bootstrap Tooltip class
34378  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34379  * to determine which dom element triggers the tooltip.
34380  * 
34381  * It needs to add support for additional attributes like tooltip-position
34382  * 
34383  * @constructor
34384  * Create a new Toolti
34385  * @param {Object} config The config object
34386  */
34387
34388 Roo.bootstrap.Tooltip = function(config){
34389     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34390     
34391     this.alignment = Roo.bootstrap.Tooltip.alignment;
34392     
34393     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34394         this.alignment = config.alignment;
34395     }
34396     
34397 };
34398
34399 Roo.apply(Roo.bootstrap.Tooltip, {
34400     /**
34401      * @function init initialize tooltip monitoring.
34402      * @static
34403      */
34404     currentEl : false,
34405     currentTip : false,
34406     currentRegion : false,
34407     
34408     //  init : delay?
34409     
34410     init : function()
34411     {
34412         Roo.get(document).on('mouseover', this.enter ,this);
34413         Roo.get(document).on('mouseout', this.leave, this);
34414          
34415         
34416         this.currentTip = new Roo.bootstrap.Tooltip();
34417     },
34418     
34419     enter : function(ev)
34420     {
34421         var dom = ev.getTarget();
34422         
34423         //Roo.log(['enter',dom]);
34424         var el = Roo.fly(dom);
34425         if (this.currentEl) {
34426             //Roo.log(dom);
34427             //Roo.log(this.currentEl);
34428             //Roo.log(this.currentEl.contains(dom));
34429             if (this.currentEl == el) {
34430                 return;
34431             }
34432             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34433                 return;
34434             }
34435
34436         }
34437         
34438         if (this.currentTip.el) {
34439             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34440         }    
34441         //Roo.log(ev);
34442         
34443         if(!el || el.dom == document){
34444             return;
34445         }
34446         
34447         var bindEl = el; 
34448         var pel = false;
34449         if (!el.attr('tooltip')) {
34450             pel = el.findParent("[tooltip]");
34451             if (pel) {
34452                 bindEl = Roo.get(pel);
34453             }
34454         }
34455         
34456        
34457         
34458         // you can not look for children, as if el is the body.. then everythign is the child..
34459         if (!pel && !el.attr('tooltip')) { //
34460             if (!el.select("[tooltip]").elements.length) {
34461                 return;
34462             }
34463             // is the mouse over this child...?
34464             bindEl = el.select("[tooltip]").first();
34465             var xy = ev.getXY();
34466             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34467                 //Roo.log("not in region.");
34468                 return;
34469             }
34470             //Roo.log("child element over..");
34471             
34472         }
34473         this.currentEl = el;
34474         this.currentTip.bind(bindEl);
34475         this.currentRegion = Roo.lib.Region.getRegion(dom);
34476         this.currentTip.enter();
34477         
34478     },
34479     leave : function(ev)
34480     {
34481         var dom = ev.getTarget();
34482         //Roo.log(['leave',dom]);
34483         if (!this.currentEl) {
34484             return;
34485         }
34486         
34487         
34488         if (dom != this.currentEl.dom) {
34489             return;
34490         }
34491         var xy = ev.getXY();
34492         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
34493             return;
34494         }
34495         // only activate leave if mouse cursor is outside... bounding box..
34496         
34497         
34498         
34499         
34500         if (this.currentTip) {
34501             this.currentTip.leave();
34502         }
34503         //Roo.log('clear currentEl');
34504         this.currentEl = false;
34505         
34506         
34507     },
34508     alignment : {
34509         'left' : ['r-l', [-2,0], 'right'],
34510         'right' : ['l-r', [2,0], 'left'],
34511         'bottom' : ['t-b', [0,2], 'top'],
34512         'top' : [ 'b-t', [0,-2], 'bottom']
34513     }
34514     
34515 });
34516
34517
34518 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
34519     
34520     
34521     bindEl : false,
34522     
34523     delay : null, // can be { show : 300 , hide: 500}
34524     
34525     timeout : null,
34526     
34527     hoverState : null, //???
34528     
34529     placement : 'bottom', 
34530     
34531     alignment : false,
34532     
34533     getAutoCreate : function(){
34534     
34535         var cfg = {
34536            cls : 'tooltip',   
34537            role : 'tooltip',
34538            cn : [
34539                 {
34540                     cls : 'tooltip-arrow arrow'
34541                 },
34542                 {
34543                     cls : 'tooltip-inner'
34544                 }
34545            ]
34546         };
34547         
34548         return cfg;
34549     },
34550     bind : function(el)
34551     {
34552         this.bindEl = el;
34553     },
34554     
34555     initEvents : function()
34556     {
34557         this.arrowEl = this.el.select('.arrow', true).first();
34558         this.innerEl = this.el.select('.tooltip-inner', true).first();
34559     },
34560     
34561     enter : function () {
34562        
34563         if (this.timeout != null) {
34564             clearTimeout(this.timeout);
34565         }
34566         
34567         this.hoverState = 'in';
34568          //Roo.log("enter - show");
34569         if (!this.delay || !this.delay.show) {
34570             this.show();
34571             return;
34572         }
34573         var _t = this;
34574         this.timeout = setTimeout(function () {
34575             if (_t.hoverState == 'in') {
34576                 _t.show();
34577             }
34578         }, this.delay.show);
34579     },
34580     leave : function()
34581     {
34582         clearTimeout(this.timeout);
34583     
34584         this.hoverState = 'out';
34585          if (!this.delay || !this.delay.hide) {
34586             this.hide();
34587             return;
34588         }
34589        
34590         var _t = this;
34591         this.timeout = setTimeout(function () {
34592             //Roo.log("leave - timeout");
34593             
34594             if (_t.hoverState == 'out') {
34595                 _t.hide();
34596                 Roo.bootstrap.Tooltip.currentEl = false;
34597             }
34598         }, delay);
34599     },
34600     
34601     show : function (msg)
34602     {
34603         if (!this.el) {
34604             this.render(document.body);
34605         }
34606         // set content.
34607         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34608         
34609         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34610         
34611         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34612         
34613         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34614                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34615
34616         if(this.bindEl.attr('tooltip-class')) {
34617             this.el.addClass(this.bindEl.attr('tooltip-class'));
34618         }
34619         
34620         var placement = typeof this.placement == 'function' ?
34621             this.placement.call(this, this.el, on_el) :
34622             this.placement;
34623         
34624         if(this.bindEl.attr('tooltip-placement')) {
34625             placement = this.bindEl.attr('tooltip-placement');
34626         }
34627             
34628         var autoToken = /\s?auto?\s?/i;
34629         var autoPlace = autoToken.test(placement);
34630         if (autoPlace) {
34631             placement = placement.replace(autoToken, '') || 'top';
34632         }
34633         
34634         //this.el.detach()
34635         //this.el.setXY([0,0]);
34636         this.el.show();
34637         //this.el.dom.style.display='block';
34638         
34639         //this.el.appendTo(on_el);
34640         
34641         var p = this.getPosition();
34642         var box = this.el.getBox();
34643         
34644         if (autoPlace) {
34645             // fixme..
34646         }
34647         
34648         var align = this.alignment[placement];
34649         
34650         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34651         
34652         if(placement == 'top' || placement == 'bottom'){
34653             if(xy[0] < 0){
34654                 placement = 'right';
34655             }
34656             
34657             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34658                 placement = 'left';
34659             }
34660             
34661             var scroll = Roo.select('body', true).first().getScroll();
34662             
34663             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34664                 placement = 'top';
34665             }
34666             
34667             align = this.alignment[placement];
34668             
34669             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34670             
34671         }
34672         
34673         var elems = document.getElementsByTagName('div');
34674         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34675         for (var i = 0; i < elems.length; i++) {
34676           var zindex = Number.parseInt(
34677                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34678                 10
34679           );
34680           if (zindex > highest) {
34681             highest = zindex;
34682           }
34683         }
34684         
34685         
34686         
34687         this.el.dom.style.zIndex = highest;
34688         
34689         this.el.alignTo(this.bindEl, align[0],align[1]);
34690         //var arrow = this.el.select('.arrow',true).first();
34691         //arrow.set(align[2], 
34692         
34693         this.el.addClass(placement);
34694         this.el.addClass("bs-tooltip-"+ placement);
34695         
34696         this.el.addClass('in fade show');
34697         
34698         this.hoverState = null;
34699         
34700         if (this.el.hasClass('fade')) {
34701             // fade it?
34702         }
34703         
34704         
34705         
34706         
34707         
34708     },
34709     hide : function()
34710     {
34711          
34712         if (!this.el) {
34713             return;
34714         }
34715         //this.el.setXY([0,0]);
34716         if(this.bindEl.attr('tooltip-class')) {
34717             this.el.removeClass(this.bindEl.attr('tooltip-class'));
34718         }
34719         this.el.removeClass(['show', 'in']);
34720         //this.el.hide();
34721         
34722     }
34723     
34724 });
34725  
34726
34727  /*
34728  * - LGPL
34729  *
34730  * Location Picker
34731  * 
34732  */
34733
34734 /**
34735  * @class Roo.bootstrap.LocationPicker
34736  * @extends Roo.bootstrap.Component
34737  * Bootstrap LocationPicker class
34738  * @cfg {Number} latitude Position when init default 0
34739  * @cfg {Number} longitude Position when init default 0
34740  * @cfg {Number} zoom default 15
34741  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34742  * @cfg {Boolean} mapTypeControl default false
34743  * @cfg {Boolean} disableDoubleClickZoom default false
34744  * @cfg {Boolean} scrollwheel default true
34745  * @cfg {Boolean} streetViewControl default false
34746  * @cfg {Number} radius default 0
34747  * @cfg {String} locationName
34748  * @cfg {Boolean} draggable default true
34749  * @cfg {Boolean} enableAutocomplete default false
34750  * @cfg {Boolean} enableReverseGeocode default true
34751  * @cfg {String} markerTitle
34752  * 
34753  * @constructor
34754  * Create a new LocationPicker
34755  * @param {Object} config The config object
34756  */
34757
34758
34759 Roo.bootstrap.LocationPicker = function(config){
34760     
34761     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34762     
34763     this.addEvents({
34764         /**
34765          * @event initial
34766          * Fires when the picker initialized.
34767          * @param {Roo.bootstrap.LocationPicker} this
34768          * @param {Google Location} location
34769          */
34770         initial : true,
34771         /**
34772          * @event positionchanged
34773          * Fires when the picker position changed.
34774          * @param {Roo.bootstrap.LocationPicker} this
34775          * @param {Google Location} location
34776          */
34777         positionchanged : true,
34778         /**
34779          * @event resize
34780          * Fires when the map resize.
34781          * @param {Roo.bootstrap.LocationPicker} this
34782          */
34783         resize : true,
34784         /**
34785          * @event show
34786          * Fires when the map show.
34787          * @param {Roo.bootstrap.LocationPicker} this
34788          */
34789         show : true,
34790         /**
34791          * @event hide
34792          * Fires when the map hide.
34793          * @param {Roo.bootstrap.LocationPicker} this
34794          */
34795         hide : true,
34796         /**
34797          * @event mapClick
34798          * Fires when click the map.
34799          * @param {Roo.bootstrap.LocationPicker} this
34800          * @param {Map event} e
34801          */
34802         mapClick : true,
34803         /**
34804          * @event mapRightClick
34805          * Fires when right click the map.
34806          * @param {Roo.bootstrap.LocationPicker} this
34807          * @param {Map event} e
34808          */
34809         mapRightClick : true,
34810         /**
34811          * @event markerClick
34812          * Fires when click the marker.
34813          * @param {Roo.bootstrap.LocationPicker} this
34814          * @param {Map event} e
34815          */
34816         markerClick : true,
34817         /**
34818          * @event markerRightClick
34819          * Fires when right click the marker.
34820          * @param {Roo.bootstrap.LocationPicker} this
34821          * @param {Map event} e
34822          */
34823         markerRightClick : true,
34824         /**
34825          * @event OverlayViewDraw
34826          * Fires when OverlayView Draw
34827          * @param {Roo.bootstrap.LocationPicker} this
34828          */
34829         OverlayViewDraw : true,
34830         /**
34831          * @event OverlayViewOnAdd
34832          * Fires when OverlayView Draw
34833          * @param {Roo.bootstrap.LocationPicker} this
34834          */
34835         OverlayViewOnAdd : true,
34836         /**
34837          * @event OverlayViewOnRemove
34838          * Fires when OverlayView Draw
34839          * @param {Roo.bootstrap.LocationPicker} this
34840          */
34841         OverlayViewOnRemove : true,
34842         /**
34843          * @event OverlayViewShow
34844          * Fires when OverlayView Draw
34845          * @param {Roo.bootstrap.LocationPicker} this
34846          * @param {Pixel} cpx
34847          */
34848         OverlayViewShow : true,
34849         /**
34850          * @event OverlayViewHide
34851          * Fires when OverlayView Draw
34852          * @param {Roo.bootstrap.LocationPicker} this
34853          */
34854         OverlayViewHide : true,
34855         /**
34856          * @event loadexception
34857          * Fires when load google lib failed.
34858          * @param {Roo.bootstrap.LocationPicker} this
34859          */
34860         loadexception : true
34861     });
34862         
34863 };
34864
34865 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
34866     
34867     gMapContext: false,
34868     
34869     latitude: 0,
34870     longitude: 0,
34871     zoom: 15,
34872     mapTypeId: false,
34873     mapTypeControl: false,
34874     disableDoubleClickZoom: false,
34875     scrollwheel: true,
34876     streetViewControl: false,
34877     radius: 0,
34878     locationName: '',
34879     draggable: true,
34880     enableAutocomplete: false,
34881     enableReverseGeocode: true,
34882     markerTitle: '',
34883     
34884     getAutoCreate: function()
34885     {
34886
34887         var cfg = {
34888             tag: 'div',
34889             cls: 'roo-location-picker'
34890         };
34891         
34892         return cfg
34893     },
34894     
34895     initEvents: function(ct, position)
34896     {       
34897         if(!this.el.getWidth() || this.isApplied()){
34898             return;
34899         }
34900         
34901         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34902         
34903         this.initial();
34904     },
34905     
34906     initial: function()
34907     {
34908         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34909             this.fireEvent('loadexception', this);
34910             return;
34911         }
34912         
34913         if(!this.mapTypeId){
34914             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34915         }
34916         
34917         this.gMapContext = this.GMapContext();
34918         
34919         this.initOverlayView();
34920         
34921         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34922         
34923         var _this = this;
34924                 
34925         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34926             _this.setPosition(_this.gMapContext.marker.position);
34927         });
34928         
34929         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34930             _this.fireEvent('mapClick', this, event);
34931             
34932         });
34933
34934         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34935             _this.fireEvent('mapRightClick', this, event);
34936             
34937         });
34938         
34939         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34940             _this.fireEvent('markerClick', this, event);
34941             
34942         });
34943
34944         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34945             _this.fireEvent('markerRightClick', this, event);
34946             
34947         });
34948         
34949         this.setPosition(this.gMapContext.location);
34950         
34951         this.fireEvent('initial', this, this.gMapContext.location);
34952     },
34953     
34954     initOverlayView: function()
34955     {
34956         var _this = this;
34957         
34958         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34959             
34960             draw: function()
34961             {
34962                 _this.fireEvent('OverlayViewDraw', _this);
34963             },
34964             
34965             onAdd: function()
34966             {
34967                 _this.fireEvent('OverlayViewOnAdd', _this);
34968             },
34969             
34970             onRemove: function()
34971             {
34972                 _this.fireEvent('OverlayViewOnRemove', _this);
34973             },
34974             
34975             show: function(cpx)
34976             {
34977                 _this.fireEvent('OverlayViewShow', _this, cpx);
34978             },
34979             
34980             hide: function()
34981             {
34982                 _this.fireEvent('OverlayViewHide', _this);
34983             }
34984             
34985         });
34986     },
34987     
34988     fromLatLngToContainerPixel: function(event)
34989     {
34990         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34991     },
34992     
34993     isApplied: function() 
34994     {
34995         return this.getGmapContext() == false ? false : true;
34996     },
34997     
34998     getGmapContext: function() 
34999     {
35000         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
35001     },
35002     
35003     GMapContext: function() 
35004     {
35005         var position = new google.maps.LatLng(this.latitude, this.longitude);
35006         
35007         var _map = new google.maps.Map(this.el.dom, {
35008             center: position,
35009             zoom: this.zoom,
35010             mapTypeId: this.mapTypeId,
35011             mapTypeControl: this.mapTypeControl,
35012             disableDoubleClickZoom: this.disableDoubleClickZoom,
35013             scrollwheel: this.scrollwheel,
35014             streetViewControl: this.streetViewControl,
35015             locationName: this.locationName,
35016             draggable: this.draggable,
35017             enableAutocomplete: this.enableAutocomplete,
35018             enableReverseGeocode: this.enableReverseGeocode
35019         });
35020         
35021         var _marker = new google.maps.Marker({
35022             position: position,
35023             map: _map,
35024             title: this.markerTitle,
35025             draggable: this.draggable
35026         });
35027         
35028         return {
35029             map: _map,
35030             marker: _marker,
35031             circle: null,
35032             location: position,
35033             radius: this.radius,
35034             locationName: this.locationName,
35035             addressComponents: {
35036                 formatted_address: null,
35037                 addressLine1: null,
35038                 addressLine2: null,
35039                 streetName: null,
35040                 streetNumber: null,
35041                 city: null,
35042                 district: null,
35043                 state: null,
35044                 stateOrProvince: null
35045             },
35046             settings: this,
35047             domContainer: this.el.dom,
35048             geodecoder: new google.maps.Geocoder()
35049         };
35050     },
35051     
35052     drawCircle: function(center, radius, options) 
35053     {
35054         if (this.gMapContext.circle != null) {
35055             this.gMapContext.circle.setMap(null);
35056         }
35057         if (radius > 0) {
35058             radius *= 1;
35059             options = Roo.apply({}, options, {
35060                 strokeColor: "#0000FF",
35061                 strokeOpacity: .35,
35062                 strokeWeight: 2,
35063                 fillColor: "#0000FF",
35064                 fillOpacity: .2
35065             });
35066             
35067             options.map = this.gMapContext.map;
35068             options.radius = radius;
35069             options.center = center;
35070             this.gMapContext.circle = new google.maps.Circle(options);
35071             return this.gMapContext.circle;
35072         }
35073         
35074         return null;
35075     },
35076     
35077     setPosition: function(location) 
35078     {
35079         this.gMapContext.location = location;
35080         this.gMapContext.marker.setPosition(location);
35081         this.gMapContext.map.panTo(location);
35082         this.drawCircle(location, this.gMapContext.radius, {});
35083         
35084         var _this = this;
35085         
35086         if (this.gMapContext.settings.enableReverseGeocode) {
35087             this.gMapContext.geodecoder.geocode({
35088                 latLng: this.gMapContext.location
35089             }, function(results, status) {
35090                 
35091                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35092                     _this.gMapContext.locationName = results[0].formatted_address;
35093                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35094                     
35095                     _this.fireEvent('positionchanged', this, location);
35096                 }
35097             });
35098             
35099             return;
35100         }
35101         
35102         this.fireEvent('positionchanged', this, location);
35103     },
35104     
35105     resize: function()
35106     {
35107         google.maps.event.trigger(this.gMapContext.map, "resize");
35108         
35109         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35110         
35111         this.fireEvent('resize', this);
35112     },
35113     
35114     setPositionByLatLng: function(latitude, longitude)
35115     {
35116         this.setPosition(new google.maps.LatLng(latitude, longitude));
35117     },
35118     
35119     getCurrentPosition: function() 
35120     {
35121         return {
35122             latitude: this.gMapContext.location.lat(),
35123             longitude: this.gMapContext.location.lng()
35124         };
35125     },
35126     
35127     getAddressName: function() 
35128     {
35129         return this.gMapContext.locationName;
35130     },
35131     
35132     getAddressComponents: function() 
35133     {
35134         return this.gMapContext.addressComponents;
35135     },
35136     
35137     address_component_from_google_geocode: function(address_components) 
35138     {
35139         var result = {};
35140         
35141         for (var i = 0; i < address_components.length; i++) {
35142             var component = address_components[i];
35143             if (component.types.indexOf("postal_code") >= 0) {
35144                 result.postalCode = component.short_name;
35145             } else if (component.types.indexOf("street_number") >= 0) {
35146                 result.streetNumber = component.short_name;
35147             } else if (component.types.indexOf("route") >= 0) {
35148                 result.streetName = component.short_name;
35149             } else if (component.types.indexOf("neighborhood") >= 0) {
35150                 result.city = component.short_name;
35151             } else if (component.types.indexOf("locality") >= 0) {
35152                 result.city = component.short_name;
35153             } else if (component.types.indexOf("sublocality") >= 0) {
35154                 result.district = component.short_name;
35155             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35156                 result.stateOrProvince = component.short_name;
35157             } else if (component.types.indexOf("country") >= 0) {
35158                 result.country = component.short_name;
35159             }
35160         }
35161         
35162         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35163         result.addressLine2 = "";
35164         return result;
35165     },
35166     
35167     setZoomLevel: function(zoom)
35168     {
35169         this.gMapContext.map.setZoom(zoom);
35170     },
35171     
35172     show: function()
35173     {
35174         if(!this.el){
35175             return;
35176         }
35177         
35178         this.el.show();
35179         
35180         this.resize();
35181         
35182         this.fireEvent('show', this);
35183     },
35184     
35185     hide: function()
35186     {
35187         if(!this.el){
35188             return;
35189         }
35190         
35191         this.el.hide();
35192         
35193         this.fireEvent('hide', this);
35194     }
35195     
35196 });
35197
35198 Roo.apply(Roo.bootstrap.LocationPicker, {
35199     
35200     OverlayView : function(map, options)
35201     {
35202         options = options || {};
35203         
35204         this.setMap(map);
35205     }
35206     
35207     
35208 });/**
35209  * @class Roo.bootstrap.Alert
35210  * @extends Roo.bootstrap.Component
35211  * Bootstrap Alert class - shows an alert area box
35212  * eg
35213  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35214   Enter a valid email address
35215 </div>
35216  * @licence LGPL
35217  * @cfg {String} title The title of alert
35218  * @cfg {String} html The content of alert
35219  * @cfg {String} weight (success|info|warning|danger) Weight of the message
35220  * @cfg {String} fa font-awesomeicon
35221  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35222  * @cfg {Boolean} close true to show a x closer
35223  * 
35224  * 
35225  * @constructor
35226  * Create a new alert
35227  * @param {Object} config The config object
35228  */
35229
35230
35231 Roo.bootstrap.Alert = function(config){
35232     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35233     
35234 };
35235
35236 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
35237     
35238     title: '',
35239     html: '',
35240     weight: false,
35241     fa: false,
35242     faicon: false, // BC
35243     close : false,
35244     
35245     
35246     getAutoCreate : function()
35247     {
35248         
35249         var cfg = {
35250             tag : 'div',
35251             cls : 'alert',
35252             cn : [
35253                 {
35254                     tag: 'button',
35255                     type :  "button",
35256                     cls: "close",
35257                     html : '×',
35258                     style : this.close ? '' : 'display:none'
35259                 },
35260                 {
35261                     tag : 'i',
35262                     cls : 'roo-alert-icon'
35263                     
35264                 },
35265                 {
35266                     tag : 'b',
35267                     cls : 'roo-alert-title',
35268                     html : this.title
35269                 },
35270                 {
35271                     tag : 'span',
35272                     cls : 'roo-alert-text',
35273                     html : this.html
35274                 }
35275             ]
35276         };
35277         
35278         if(this.faicon){
35279             cfg.cn[0].cls += ' fa ' + this.faicon;
35280         }
35281         if(this.fa){
35282             cfg.cn[0].cls += ' fa ' + this.fa;
35283         }
35284         
35285         if(this.weight){
35286             cfg.cls += ' alert-' + this.weight;
35287         }
35288         
35289         return cfg;
35290     },
35291     
35292     initEvents: function() 
35293     {
35294         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35295         this.titleEl =  this.el.select('.roo-alert-title',true).first();
35296         this.iconEl = this.el.select('.roo-alert-icon',true).first();
35297         this.htmlEl = this.el.select('.roo-alert-text',true).first();
35298         if (this.seconds > 0) {
35299             this.hide.defer(this.seconds, this);
35300         }
35301     },
35302     /**
35303      * Set the Title Message HTML
35304      * @param {String} html
35305      */
35306     setTitle : function(str)
35307     {
35308         this.titleEl.dom.innerHTML = str;
35309     },
35310      
35311      /**
35312      * Set the Body Message HTML
35313      * @param {String} html
35314      */
35315     setHtml : function(str)
35316     {
35317         this.htmlEl.dom.innerHTML = str;
35318     },
35319     /**
35320      * Set the Weight of the alert
35321      * @param {String} (success|info|warning|danger) weight
35322      */
35323     
35324     setWeight : function(weight)
35325     {
35326         if(this.weight){
35327             this.el.removeClass('alert-' + this.weight);
35328         }
35329         
35330         this.weight = weight;
35331         
35332         this.el.addClass('alert-' + this.weight);
35333     },
35334       /**
35335      * Set the Icon of the alert
35336      * @param {String} see fontawsome names (name without the 'fa-' bit)
35337      */
35338     setIcon : function(icon)
35339     {
35340         if(this.faicon){
35341             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35342         }
35343         
35344         this.faicon = icon;
35345         
35346         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35347     },
35348     /**
35349      * Hide the Alert
35350      */
35351     hide: function() 
35352     {
35353         this.el.hide();   
35354     },
35355     /**
35356      * Show the Alert
35357      */
35358     show: function() 
35359     {  
35360         this.el.show();   
35361     }
35362     
35363 });
35364
35365  
35366 /*
35367 * Licence: LGPL
35368 */
35369
35370 /**
35371  * @class Roo.bootstrap.UploadCropbox
35372  * @extends Roo.bootstrap.Component
35373  * Bootstrap UploadCropbox class
35374  * @cfg {String} emptyText show when image has been loaded
35375  * @cfg {String} rotateNotify show when image too small to rotate
35376  * @cfg {Number} errorTimeout default 3000
35377  * @cfg {Number} minWidth default 300
35378  * @cfg {Number} minHeight default 300
35379  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35380  * @cfg {Boolean} isDocument (true|false) default false
35381  * @cfg {String} url action url
35382  * @cfg {String} paramName default 'imageUpload'
35383  * @cfg {String} method default POST
35384  * @cfg {Boolean} loadMask (true|false) default true
35385  * @cfg {Boolean} loadingText default 'Loading...'
35386  * 
35387  * @constructor
35388  * Create a new UploadCropbox
35389  * @param {Object} config The config object
35390  */
35391
35392 Roo.bootstrap.UploadCropbox = function(config){
35393     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35394     
35395     this.addEvents({
35396         /**
35397          * @event beforeselectfile
35398          * Fire before select file
35399          * @param {Roo.bootstrap.UploadCropbox} this
35400          */
35401         "beforeselectfile" : true,
35402         /**
35403          * @event initial
35404          * Fire after initEvent
35405          * @param {Roo.bootstrap.UploadCropbox} this
35406          */
35407         "initial" : true,
35408         /**
35409          * @event crop
35410          * Fire after initEvent
35411          * @param {Roo.bootstrap.UploadCropbox} this
35412          * @param {String} data
35413          */
35414         "crop" : true,
35415         /**
35416          * @event prepare
35417          * Fire when preparing the file data
35418          * @param {Roo.bootstrap.UploadCropbox} this
35419          * @param {Object} file
35420          */
35421         "prepare" : true,
35422         /**
35423          * @event exception
35424          * Fire when get exception
35425          * @param {Roo.bootstrap.UploadCropbox} this
35426          * @param {XMLHttpRequest} xhr
35427          */
35428         "exception" : true,
35429         /**
35430          * @event beforeloadcanvas
35431          * Fire before load the canvas
35432          * @param {Roo.bootstrap.UploadCropbox} this
35433          * @param {String} src
35434          */
35435         "beforeloadcanvas" : true,
35436         /**
35437          * @event trash
35438          * Fire when trash image
35439          * @param {Roo.bootstrap.UploadCropbox} this
35440          */
35441         "trash" : true,
35442         /**
35443          * @event download
35444          * Fire when download the image
35445          * @param {Roo.bootstrap.UploadCropbox} this
35446          */
35447         "download" : true,
35448         /**
35449          * @event footerbuttonclick
35450          * Fire when footerbuttonclick
35451          * @param {Roo.bootstrap.UploadCropbox} this
35452          * @param {String} type
35453          */
35454         "footerbuttonclick" : true,
35455         /**
35456          * @event resize
35457          * Fire when resize
35458          * @param {Roo.bootstrap.UploadCropbox} this
35459          */
35460         "resize" : true,
35461         /**
35462          * @event rotate
35463          * Fire when rotate the image
35464          * @param {Roo.bootstrap.UploadCropbox} this
35465          * @param {String} pos
35466          */
35467         "rotate" : true,
35468         /**
35469          * @event inspect
35470          * Fire when inspect the file
35471          * @param {Roo.bootstrap.UploadCropbox} this
35472          * @param {Object} file
35473          */
35474         "inspect" : true,
35475         /**
35476          * @event upload
35477          * Fire when xhr upload the file
35478          * @param {Roo.bootstrap.UploadCropbox} this
35479          * @param {Object} data
35480          */
35481         "upload" : true,
35482         /**
35483          * @event arrange
35484          * Fire when arrange the file data
35485          * @param {Roo.bootstrap.UploadCropbox} this
35486          * @param {Object} formData
35487          */
35488         "arrange" : true
35489     });
35490     
35491     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35492 };
35493
35494 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
35495     
35496     emptyText : 'Click to upload image',
35497     rotateNotify : 'Image is too small to rotate',
35498     errorTimeout : 3000,
35499     scale : 0,
35500     baseScale : 1,
35501     rotate : 0,
35502     dragable : false,
35503     pinching : false,
35504     mouseX : 0,
35505     mouseY : 0,
35506     cropData : false,
35507     minWidth : 300,
35508     minHeight : 300,
35509     file : false,
35510     exif : {},
35511     baseRotate : 1,
35512     cropType : 'image/jpeg',
35513     buttons : false,
35514     canvasLoaded : false,
35515     isDocument : false,
35516     method : 'POST',
35517     paramName : 'imageUpload',
35518     loadMask : true,
35519     loadingText : 'Loading...',
35520     maskEl : false,
35521     
35522     getAutoCreate : function()
35523     {
35524         var cfg = {
35525             tag : 'div',
35526             cls : 'roo-upload-cropbox',
35527             cn : [
35528                 {
35529                     tag : 'input',
35530                     cls : 'roo-upload-cropbox-selector',
35531                     type : 'file'
35532                 },
35533                 {
35534                     tag : 'div',
35535                     cls : 'roo-upload-cropbox-body',
35536                     style : 'cursor:pointer',
35537                     cn : [
35538                         {
35539                             tag : 'div',
35540                             cls : 'roo-upload-cropbox-preview'
35541                         },
35542                         {
35543                             tag : 'div',
35544                             cls : 'roo-upload-cropbox-thumb'
35545                         },
35546                         {
35547                             tag : 'div',
35548                             cls : 'roo-upload-cropbox-empty-notify',
35549                             html : this.emptyText
35550                         },
35551                         {
35552                             tag : 'div',
35553                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35554                             html : this.rotateNotify
35555                         }
35556                     ]
35557                 },
35558                 {
35559                     tag : 'div',
35560                     cls : 'roo-upload-cropbox-footer',
35561                     cn : {
35562                         tag : 'div',
35563                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35564                         cn : []
35565                     }
35566                 }
35567             ]
35568         };
35569         
35570         return cfg;
35571     },
35572     
35573     onRender : function(ct, position)
35574     {
35575         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35576         
35577         if (this.buttons.length) {
35578             
35579             Roo.each(this.buttons, function(bb) {
35580                 
35581                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35582                 
35583                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35584                 
35585             }, this);
35586         }
35587         
35588         if(this.loadMask){
35589             this.maskEl = this.el;
35590         }
35591     },
35592     
35593     initEvents : function()
35594     {
35595         this.urlAPI = (window.createObjectURL && window) || 
35596                                 (window.URL && URL.revokeObjectURL && URL) || 
35597                                 (window.webkitURL && webkitURL);
35598                         
35599         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35600         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35601         
35602         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35603         this.selectorEl.hide();
35604         
35605         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35606         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35607         
35608         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35609         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35610         this.thumbEl.hide();
35611         
35612         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35613         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35614         
35615         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35616         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35617         this.errorEl.hide();
35618         
35619         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35620         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35621         this.footerEl.hide();
35622         
35623         this.setThumbBoxSize();
35624         
35625         this.bind();
35626         
35627         this.resize();
35628         
35629         this.fireEvent('initial', this);
35630     },
35631
35632     bind : function()
35633     {
35634         var _this = this;
35635         
35636         window.addEventListener("resize", function() { _this.resize(); } );
35637         
35638         this.bodyEl.on('click', this.beforeSelectFile, this);
35639         
35640         if(Roo.isTouch){
35641             this.bodyEl.on('touchstart', this.onTouchStart, this);
35642             this.bodyEl.on('touchmove', this.onTouchMove, this);
35643             this.bodyEl.on('touchend', this.onTouchEnd, this);
35644         }
35645         
35646         if(!Roo.isTouch){
35647             this.bodyEl.on('mousedown', this.onMouseDown, this);
35648             this.bodyEl.on('mousemove', this.onMouseMove, this);
35649             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35650             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35651             Roo.get(document).on('mouseup', this.onMouseUp, this);
35652         }
35653         
35654         this.selectorEl.on('change', this.onFileSelected, this);
35655     },
35656     
35657     reset : function()
35658     {    
35659         this.scale = 0;
35660         this.baseScale = 1;
35661         this.rotate = 0;
35662         this.baseRotate = 1;
35663         this.dragable = false;
35664         this.pinching = false;
35665         this.mouseX = 0;
35666         this.mouseY = 0;
35667         this.cropData = false;
35668         this.notifyEl.dom.innerHTML = this.emptyText;
35669         
35670         this.selectorEl.dom.value = '';
35671         
35672     },
35673     
35674     resize : function()
35675     {
35676         if(this.fireEvent('resize', this) != false){
35677             this.setThumbBoxPosition();
35678             this.setCanvasPosition();
35679         }
35680     },
35681     
35682     onFooterButtonClick : function(e, el, o, type)
35683     {
35684         switch (type) {
35685             case 'rotate-left' :
35686                 this.onRotateLeft(e);
35687                 break;
35688             case 'rotate-right' :
35689                 this.onRotateRight(e);
35690                 break;
35691             case 'picture' :
35692                 this.beforeSelectFile(e);
35693                 break;
35694             case 'trash' :
35695                 this.trash(e);
35696                 break;
35697             case 'crop' :
35698                 this.crop(e);
35699                 break;
35700             case 'download' :
35701                 this.download(e);
35702                 break;
35703             default :
35704                 break;
35705         }
35706         
35707         this.fireEvent('footerbuttonclick', this, type);
35708     },
35709     
35710     beforeSelectFile : function(e)
35711     {
35712         e.preventDefault();
35713         
35714         if(this.fireEvent('beforeselectfile', this) != false){
35715             this.selectorEl.dom.click();
35716         }
35717     },
35718     
35719     onFileSelected : function(e)
35720     {
35721         e.preventDefault();
35722         
35723         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35724             return;
35725         }
35726         
35727         var file = this.selectorEl.dom.files[0];
35728         
35729         if(this.fireEvent('inspect', this, file) != false){
35730             this.prepare(file);
35731         }
35732         
35733     },
35734     
35735     trash : function(e)
35736     {
35737         this.fireEvent('trash', this);
35738     },
35739     
35740     download : function(e)
35741     {
35742         this.fireEvent('download', this);
35743     },
35744     
35745     loadCanvas : function(src)
35746     {   
35747         if(this.fireEvent('beforeloadcanvas', this, src) != false){
35748             
35749             this.reset();
35750             
35751             this.imageEl = document.createElement('img');
35752             
35753             var _this = this;
35754             
35755             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35756             
35757             this.imageEl.src = src;
35758         }
35759     },
35760     
35761     onLoadCanvas : function()
35762     {   
35763         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35764         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35765         
35766         this.bodyEl.un('click', this.beforeSelectFile, this);
35767         
35768         this.notifyEl.hide();
35769         this.thumbEl.show();
35770         this.footerEl.show();
35771         
35772         this.baseRotateLevel();
35773         
35774         if(this.isDocument){
35775             this.setThumbBoxSize();
35776         }
35777         
35778         this.setThumbBoxPosition();
35779         
35780         this.baseScaleLevel();
35781         
35782         this.draw();
35783         
35784         this.resize();
35785         
35786         this.canvasLoaded = true;
35787         
35788         if(this.loadMask){
35789             this.maskEl.unmask();
35790         }
35791         
35792     },
35793     
35794     setCanvasPosition : function()
35795     {   
35796         if(!this.canvasEl){
35797             return;
35798         }
35799         
35800         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35801         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35802         
35803         this.previewEl.setLeft(pw);
35804         this.previewEl.setTop(ph);
35805         
35806     },
35807     
35808     onMouseDown : function(e)
35809     {   
35810         e.stopEvent();
35811         
35812         this.dragable = true;
35813         this.pinching = false;
35814         
35815         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35816             this.dragable = false;
35817             return;
35818         }
35819         
35820         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35821         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35822         
35823     },
35824     
35825     onMouseMove : function(e)
35826     {   
35827         e.stopEvent();
35828         
35829         if(!this.canvasLoaded){
35830             return;
35831         }
35832         
35833         if (!this.dragable){
35834             return;
35835         }
35836         
35837         var minX = Math.ceil(this.thumbEl.getLeft(true));
35838         var minY = Math.ceil(this.thumbEl.getTop(true));
35839         
35840         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35841         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35842         
35843         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35844         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35845         
35846         x = x - this.mouseX;
35847         y = y - this.mouseY;
35848         
35849         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35850         var bgY = Math.ceil(y + this.previewEl.getTop(true));
35851         
35852         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35853         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35854         
35855         this.previewEl.setLeft(bgX);
35856         this.previewEl.setTop(bgY);
35857         
35858         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35859         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35860     },
35861     
35862     onMouseUp : function(e)
35863     {   
35864         e.stopEvent();
35865         
35866         this.dragable = false;
35867     },
35868     
35869     onMouseWheel : function(e)
35870     {   
35871         e.stopEvent();
35872         
35873         this.startScale = this.scale;
35874         
35875         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35876         
35877         if(!this.zoomable()){
35878             this.scale = this.startScale;
35879             return;
35880         }
35881         
35882         this.draw();
35883         
35884         return;
35885     },
35886     
35887     zoomable : function()
35888     {
35889         var minScale = this.thumbEl.getWidth() / this.minWidth;
35890         
35891         if(this.minWidth < this.minHeight){
35892             minScale = this.thumbEl.getHeight() / this.minHeight;
35893         }
35894         
35895         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35896         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35897         
35898         if(
35899                 this.isDocument &&
35900                 (this.rotate == 0 || this.rotate == 180) && 
35901                 (
35902                     width > this.imageEl.OriginWidth || 
35903                     height > this.imageEl.OriginHeight ||
35904                     (width < this.minWidth && height < this.minHeight)
35905                 )
35906         ){
35907             return false;
35908         }
35909         
35910         if(
35911                 this.isDocument &&
35912                 (this.rotate == 90 || this.rotate == 270) && 
35913                 (
35914                     width > this.imageEl.OriginWidth || 
35915                     height > this.imageEl.OriginHeight ||
35916                     (width < this.minHeight && height < this.minWidth)
35917                 )
35918         ){
35919             return false;
35920         }
35921         
35922         if(
35923                 !this.isDocument &&
35924                 (this.rotate == 0 || this.rotate == 180) && 
35925                 (
35926                     width < this.minWidth || 
35927                     width > this.imageEl.OriginWidth || 
35928                     height < this.minHeight || 
35929                     height > this.imageEl.OriginHeight
35930                 )
35931         ){
35932             return false;
35933         }
35934         
35935         if(
35936                 !this.isDocument &&
35937                 (this.rotate == 90 || this.rotate == 270) && 
35938                 (
35939                     width < this.minHeight || 
35940                     width > this.imageEl.OriginWidth || 
35941                     height < this.minWidth || 
35942                     height > this.imageEl.OriginHeight
35943                 )
35944         ){
35945             return false;
35946         }
35947         
35948         return true;
35949         
35950     },
35951     
35952     onRotateLeft : function(e)
35953     {   
35954         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35955             
35956             var minScale = this.thumbEl.getWidth() / this.minWidth;
35957             
35958             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35959             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35960             
35961             this.startScale = this.scale;
35962             
35963             while (this.getScaleLevel() < minScale){
35964             
35965                 this.scale = this.scale + 1;
35966                 
35967                 if(!this.zoomable()){
35968                     break;
35969                 }
35970                 
35971                 if(
35972                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35973                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35974                 ){
35975                     continue;
35976                 }
35977                 
35978                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35979
35980                 this.draw();
35981                 
35982                 return;
35983             }
35984             
35985             this.scale = this.startScale;
35986             
35987             this.onRotateFail();
35988             
35989             return false;
35990         }
35991         
35992         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35993
35994         if(this.isDocument){
35995             this.setThumbBoxSize();
35996             this.setThumbBoxPosition();
35997             this.setCanvasPosition();
35998         }
35999         
36000         this.draw();
36001         
36002         this.fireEvent('rotate', this, 'left');
36003         
36004     },
36005     
36006     onRotateRight : function(e)
36007     {
36008         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36009             
36010             var minScale = this.thumbEl.getWidth() / this.minWidth;
36011         
36012             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36013             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36014             
36015             this.startScale = this.scale;
36016             
36017             while (this.getScaleLevel() < minScale){
36018             
36019                 this.scale = this.scale + 1;
36020                 
36021                 if(!this.zoomable()){
36022                     break;
36023                 }
36024                 
36025                 if(
36026                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36027                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36028                 ){
36029                     continue;
36030                 }
36031                 
36032                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36033
36034                 this.draw();
36035                 
36036                 return;
36037             }
36038             
36039             this.scale = this.startScale;
36040             
36041             this.onRotateFail();
36042             
36043             return false;
36044         }
36045         
36046         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36047
36048         if(this.isDocument){
36049             this.setThumbBoxSize();
36050             this.setThumbBoxPosition();
36051             this.setCanvasPosition();
36052         }
36053         
36054         this.draw();
36055         
36056         this.fireEvent('rotate', this, 'right');
36057     },
36058     
36059     onRotateFail : function()
36060     {
36061         this.errorEl.show(true);
36062         
36063         var _this = this;
36064         
36065         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36066     },
36067     
36068     draw : function()
36069     {
36070         this.previewEl.dom.innerHTML = '';
36071         
36072         var canvasEl = document.createElement("canvas");
36073         
36074         var contextEl = canvasEl.getContext("2d");
36075         
36076         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36077         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36078         var center = this.imageEl.OriginWidth / 2;
36079         
36080         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36081             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36082             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36083             center = this.imageEl.OriginHeight / 2;
36084         }
36085         
36086         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36087         
36088         contextEl.translate(center, center);
36089         contextEl.rotate(this.rotate * Math.PI / 180);
36090
36091         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36092         
36093         this.canvasEl = document.createElement("canvas");
36094         
36095         this.contextEl = this.canvasEl.getContext("2d");
36096         
36097         switch (this.rotate) {
36098             case 0 :
36099                 
36100                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36101                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36102                 
36103                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36104                 
36105                 break;
36106             case 90 : 
36107                 
36108                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36109                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36110                 
36111                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36112                     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);
36113                     break;
36114                 }
36115                 
36116                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36117                 
36118                 break;
36119             case 180 :
36120                 
36121                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36122                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36123                 
36124                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36125                     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);
36126                     break;
36127                 }
36128                 
36129                 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);
36130                 
36131                 break;
36132             case 270 :
36133                 
36134                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36135                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36136         
36137                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36138                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36139                     break;
36140                 }
36141                 
36142                 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);
36143                 
36144                 break;
36145             default : 
36146                 break;
36147         }
36148         
36149         this.previewEl.appendChild(this.canvasEl);
36150         
36151         this.setCanvasPosition();
36152     },
36153     
36154     crop : function()
36155     {
36156         if(!this.canvasLoaded){
36157             return;
36158         }
36159         
36160         var imageCanvas = document.createElement("canvas");
36161         
36162         var imageContext = imageCanvas.getContext("2d");
36163         
36164         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36165         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36166         
36167         var center = imageCanvas.width / 2;
36168         
36169         imageContext.translate(center, center);
36170         
36171         imageContext.rotate(this.rotate * Math.PI / 180);
36172         
36173         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36174         
36175         var canvas = document.createElement("canvas");
36176         
36177         var context = canvas.getContext("2d");
36178                 
36179         canvas.width = this.minWidth;
36180         canvas.height = this.minHeight;
36181
36182         switch (this.rotate) {
36183             case 0 :
36184                 
36185                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36186                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36187                 
36188                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36189                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36190                 
36191                 var targetWidth = this.minWidth - 2 * x;
36192                 var targetHeight = this.minHeight - 2 * y;
36193                 
36194                 var scale = 1;
36195                 
36196                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36197                     scale = targetWidth / width;
36198                 }
36199                 
36200                 if(x > 0 && y == 0){
36201                     scale = targetHeight / height;
36202                 }
36203                 
36204                 if(x > 0 && y > 0){
36205                     scale = targetWidth / width;
36206                     
36207                     if(width < height){
36208                         scale = targetHeight / height;
36209                     }
36210                 }
36211                 
36212                 context.scale(scale, scale);
36213                 
36214                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36215                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36216
36217                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36218                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36219
36220                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36221                 
36222                 break;
36223             case 90 : 
36224                 
36225                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36226                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36227                 
36228                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36229                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36230                 
36231                 var targetWidth = this.minWidth - 2 * x;
36232                 var targetHeight = this.minHeight - 2 * y;
36233                 
36234                 var scale = 1;
36235                 
36236                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36237                     scale = targetWidth / width;
36238                 }
36239                 
36240                 if(x > 0 && y == 0){
36241                     scale = targetHeight / height;
36242                 }
36243                 
36244                 if(x > 0 && y > 0){
36245                     scale = targetWidth / width;
36246                     
36247                     if(width < height){
36248                         scale = targetHeight / height;
36249                     }
36250                 }
36251                 
36252                 context.scale(scale, scale);
36253                 
36254                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36255                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36256
36257                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36258                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36259                 
36260                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36261                 
36262                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36263                 
36264                 break;
36265             case 180 :
36266                 
36267                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36268                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36269                 
36270                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36271                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36272                 
36273                 var targetWidth = this.minWidth - 2 * x;
36274                 var targetHeight = this.minHeight - 2 * y;
36275                 
36276                 var scale = 1;
36277                 
36278                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36279                     scale = targetWidth / width;
36280                 }
36281                 
36282                 if(x > 0 && y == 0){
36283                     scale = targetHeight / height;
36284                 }
36285                 
36286                 if(x > 0 && y > 0){
36287                     scale = targetWidth / width;
36288                     
36289                     if(width < height){
36290                         scale = targetHeight / height;
36291                     }
36292                 }
36293                 
36294                 context.scale(scale, scale);
36295                 
36296                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36297                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36298
36299                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36300                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36301
36302                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36303                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36304                 
36305                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36306                 
36307                 break;
36308             case 270 :
36309                 
36310                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36311                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36312                 
36313                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36314                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36315                 
36316                 var targetWidth = this.minWidth - 2 * x;
36317                 var targetHeight = this.minHeight - 2 * y;
36318                 
36319                 var scale = 1;
36320                 
36321                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36322                     scale = targetWidth / width;
36323                 }
36324                 
36325                 if(x > 0 && y == 0){
36326                     scale = targetHeight / height;
36327                 }
36328                 
36329                 if(x > 0 && y > 0){
36330                     scale = targetWidth / width;
36331                     
36332                     if(width < height){
36333                         scale = targetHeight / height;
36334                     }
36335                 }
36336                 
36337                 context.scale(scale, scale);
36338                 
36339                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36340                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36341
36342                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36343                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36344                 
36345                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36346                 
36347                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36348                 
36349                 break;
36350             default : 
36351                 break;
36352         }
36353         
36354         this.cropData = canvas.toDataURL(this.cropType);
36355         
36356         if(this.fireEvent('crop', this, this.cropData) !== false){
36357             this.process(this.file, this.cropData);
36358         }
36359         
36360         return;
36361         
36362     },
36363     
36364     setThumbBoxSize : function()
36365     {
36366         var width, height;
36367         
36368         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36369             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36370             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36371             
36372             this.minWidth = width;
36373             this.minHeight = height;
36374             
36375             if(this.rotate == 90 || this.rotate == 270){
36376                 this.minWidth = height;
36377                 this.minHeight = width;
36378             }
36379         }
36380         
36381         height = 300;
36382         width = Math.ceil(this.minWidth * height / this.minHeight);
36383         
36384         if(this.minWidth > this.minHeight){
36385             width = 300;
36386             height = Math.ceil(this.minHeight * width / this.minWidth);
36387         }
36388         
36389         this.thumbEl.setStyle({
36390             width : width + 'px',
36391             height : height + 'px'
36392         });
36393
36394         return;
36395             
36396     },
36397     
36398     setThumbBoxPosition : function()
36399     {
36400         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36401         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36402         
36403         this.thumbEl.setLeft(x);
36404         this.thumbEl.setTop(y);
36405         
36406     },
36407     
36408     baseRotateLevel : function()
36409     {
36410         this.baseRotate = 1;
36411         
36412         if(
36413                 typeof(this.exif) != 'undefined' &&
36414                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36415                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36416         ){
36417             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36418         }
36419         
36420         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36421         
36422     },
36423     
36424     baseScaleLevel : function()
36425     {
36426         var width, height;
36427         
36428         if(this.isDocument){
36429             
36430             if(this.baseRotate == 6 || this.baseRotate == 8){
36431             
36432                 height = this.thumbEl.getHeight();
36433                 this.baseScale = height / this.imageEl.OriginWidth;
36434
36435                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36436                     width = this.thumbEl.getWidth();
36437                     this.baseScale = width / this.imageEl.OriginHeight;
36438                 }
36439
36440                 return;
36441             }
36442
36443             height = this.thumbEl.getHeight();
36444             this.baseScale = height / this.imageEl.OriginHeight;
36445
36446             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36447                 width = this.thumbEl.getWidth();
36448                 this.baseScale = width / this.imageEl.OriginWidth;
36449             }
36450
36451             return;
36452         }
36453         
36454         if(this.baseRotate == 6 || this.baseRotate == 8){
36455             
36456             width = this.thumbEl.getHeight();
36457             this.baseScale = width / this.imageEl.OriginHeight;
36458             
36459             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36460                 height = this.thumbEl.getWidth();
36461                 this.baseScale = height / this.imageEl.OriginHeight;
36462             }
36463             
36464             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36465                 height = this.thumbEl.getWidth();
36466                 this.baseScale = height / this.imageEl.OriginHeight;
36467                 
36468                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36469                     width = this.thumbEl.getHeight();
36470                     this.baseScale = width / this.imageEl.OriginWidth;
36471                 }
36472             }
36473             
36474             return;
36475         }
36476         
36477         width = this.thumbEl.getWidth();
36478         this.baseScale = width / this.imageEl.OriginWidth;
36479         
36480         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36481             height = this.thumbEl.getHeight();
36482             this.baseScale = height / this.imageEl.OriginHeight;
36483         }
36484         
36485         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36486             
36487             height = this.thumbEl.getHeight();
36488             this.baseScale = height / this.imageEl.OriginHeight;
36489             
36490             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36491                 width = this.thumbEl.getWidth();
36492                 this.baseScale = width / this.imageEl.OriginWidth;
36493             }
36494             
36495         }
36496         
36497         return;
36498     },
36499     
36500     getScaleLevel : function()
36501     {
36502         return this.baseScale * Math.pow(1.1, this.scale);
36503     },
36504     
36505     onTouchStart : function(e)
36506     {
36507         if(!this.canvasLoaded){
36508             this.beforeSelectFile(e);
36509             return;
36510         }
36511         
36512         var touches = e.browserEvent.touches;
36513         
36514         if(!touches){
36515             return;
36516         }
36517         
36518         if(touches.length == 1){
36519             this.onMouseDown(e);
36520             return;
36521         }
36522         
36523         if(touches.length != 2){
36524             return;
36525         }
36526         
36527         var coords = [];
36528         
36529         for(var i = 0, finger; finger = touches[i]; i++){
36530             coords.push(finger.pageX, finger.pageY);
36531         }
36532         
36533         var x = Math.pow(coords[0] - coords[2], 2);
36534         var y = Math.pow(coords[1] - coords[3], 2);
36535         
36536         this.startDistance = Math.sqrt(x + y);
36537         
36538         this.startScale = this.scale;
36539         
36540         this.pinching = true;
36541         this.dragable = false;
36542         
36543     },
36544     
36545     onTouchMove : function(e)
36546     {
36547         if(!this.pinching && !this.dragable){
36548             return;
36549         }
36550         
36551         var touches = e.browserEvent.touches;
36552         
36553         if(!touches){
36554             return;
36555         }
36556         
36557         if(this.dragable){
36558             this.onMouseMove(e);
36559             return;
36560         }
36561         
36562         var coords = [];
36563         
36564         for(var i = 0, finger; finger = touches[i]; i++){
36565             coords.push(finger.pageX, finger.pageY);
36566         }
36567         
36568         var x = Math.pow(coords[0] - coords[2], 2);
36569         var y = Math.pow(coords[1] - coords[3], 2);
36570         
36571         this.endDistance = Math.sqrt(x + y);
36572         
36573         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36574         
36575         if(!this.zoomable()){
36576             this.scale = this.startScale;
36577             return;
36578         }
36579         
36580         this.draw();
36581         
36582     },
36583     
36584     onTouchEnd : function(e)
36585     {
36586         this.pinching = false;
36587         this.dragable = false;
36588         
36589     },
36590     
36591     process : function(file, crop)
36592     {
36593         if(this.loadMask){
36594             this.maskEl.mask(this.loadingText);
36595         }
36596         
36597         this.xhr = new XMLHttpRequest();
36598         
36599         file.xhr = this.xhr;
36600
36601         this.xhr.open(this.method, this.url, true);
36602         
36603         var headers = {
36604             "Accept": "application/json",
36605             "Cache-Control": "no-cache",
36606             "X-Requested-With": "XMLHttpRequest"
36607         };
36608         
36609         for (var headerName in headers) {
36610             var headerValue = headers[headerName];
36611             if (headerValue) {
36612                 this.xhr.setRequestHeader(headerName, headerValue);
36613             }
36614         }
36615         
36616         var _this = this;
36617         
36618         this.xhr.onload = function()
36619         {
36620             _this.xhrOnLoad(_this.xhr);
36621         }
36622         
36623         this.xhr.onerror = function()
36624         {
36625             _this.xhrOnError(_this.xhr);
36626         }
36627         
36628         var formData = new FormData();
36629
36630         formData.append('returnHTML', 'NO');
36631         
36632         if(crop){
36633             formData.append('crop', crop);
36634         }
36635         
36636         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36637             formData.append(this.paramName, file, file.name);
36638         }
36639         
36640         if(typeof(file.filename) != 'undefined'){
36641             formData.append('filename', file.filename);
36642         }
36643         
36644         if(typeof(file.mimetype) != 'undefined'){
36645             formData.append('mimetype', file.mimetype);
36646         }
36647         
36648         if(this.fireEvent('arrange', this, formData) != false){
36649             this.xhr.send(formData);
36650         };
36651     },
36652     
36653     xhrOnLoad : function(xhr)
36654     {
36655         if(this.loadMask){
36656             this.maskEl.unmask();
36657         }
36658         
36659         if (xhr.readyState !== 4) {
36660             this.fireEvent('exception', this, xhr);
36661             return;
36662         }
36663
36664         var response = Roo.decode(xhr.responseText);
36665         
36666         if(!response.success){
36667             this.fireEvent('exception', this, xhr);
36668             return;
36669         }
36670         
36671         var response = Roo.decode(xhr.responseText);
36672         
36673         this.fireEvent('upload', this, response);
36674         
36675     },
36676     
36677     xhrOnError : function()
36678     {
36679         if(this.loadMask){
36680             this.maskEl.unmask();
36681         }
36682         
36683         Roo.log('xhr on error');
36684         
36685         var response = Roo.decode(xhr.responseText);
36686           
36687         Roo.log(response);
36688         
36689     },
36690     
36691     prepare : function(file)
36692     {   
36693         if(this.loadMask){
36694             this.maskEl.mask(this.loadingText);
36695         }
36696         
36697         this.file = false;
36698         this.exif = {};
36699         
36700         if(typeof(file) === 'string'){
36701             this.loadCanvas(file);
36702             return;
36703         }
36704         
36705         if(!file || !this.urlAPI){
36706             return;
36707         }
36708         
36709         this.file = file;
36710         this.cropType = file.type;
36711         
36712         var _this = this;
36713         
36714         if(this.fireEvent('prepare', this, this.file) != false){
36715             
36716             var reader = new FileReader();
36717             
36718             reader.onload = function (e) {
36719                 if (e.target.error) {
36720                     Roo.log(e.target.error);
36721                     return;
36722                 }
36723                 
36724                 var buffer = e.target.result,
36725                     dataView = new DataView(buffer),
36726                     offset = 2,
36727                     maxOffset = dataView.byteLength - 4,
36728                     markerBytes,
36729                     markerLength;
36730                 
36731                 if (dataView.getUint16(0) === 0xffd8) {
36732                     while (offset < maxOffset) {
36733                         markerBytes = dataView.getUint16(offset);
36734                         
36735                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36736                             markerLength = dataView.getUint16(offset + 2) + 2;
36737                             if (offset + markerLength > dataView.byteLength) {
36738                                 Roo.log('Invalid meta data: Invalid segment size.');
36739                                 break;
36740                             }
36741                             
36742                             if(markerBytes == 0xffe1){
36743                                 _this.parseExifData(
36744                                     dataView,
36745                                     offset,
36746                                     markerLength
36747                                 );
36748                             }
36749                             
36750                             offset += markerLength;
36751                             
36752                             continue;
36753                         }
36754                         
36755                         break;
36756                     }
36757                     
36758                 }
36759                 
36760                 var url = _this.urlAPI.createObjectURL(_this.file);
36761                 
36762                 _this.loadCanvas(url);
36763                 
36764                 return;
36765             }
36766             
36767             reader.readAsArrayBuffer(this.file);
36768             
36769         }
36770         
36771     },
36772     
36773     parseExifData : function(dataView, offset, length)
36774     {
36775         var tiffOffset = offset + 10,
36776             littleEndian,
36777             dirOffset;
36778     
36779         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36780             // No Exif data, might be XMP data instead
36781             return;
36782         }
36783         
36784         // Check for the ASCII code for "Exif" (0x45786966):
36785         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36786             // No Exif data, might be XMP data instead
36787             return;
36788         }
36789         if (tiffOffset + 8 > dataView.byteLength) {
36790             Roo.log('Invalid Exif data: Invalid segment size.');
36791             return;
36792         }
36793         // Check for the two null bytes:
36794         if (dataView.getUint16(offset + 8) !== 0x0000) {
36795             Roo.log('Invalid Exif data: Missing byte alignment offset.');
36796             return;
36797         }
36798         // Check the byte alignment:
36799         switch (dataView.getUint16(tiffOffset)) {
36800         case 0x4949:
36801             littleEndian = true;
36802             break;
36803         case 0x4D4D:
36804             littleEndian = false;
36805             break;
36806         default:
36807             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36808             return;
36809         }
36810         // Check for the TIFF tag marker (0x002A):
36811         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36812             Roo.log('Invalid Exif data: Missing TIFF marker.');
36813             return;
36814         }
36815         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36816         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36817         
36818         this.parseExifTags(
36819             dataView,
36820             tiffOffset,
36821             tiffOffset + dirOffset,
36822             littleEndian
36823         );
36824     },
36825     
36826     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36827     {
36828         var tagsNumber,
36829             dirEndOffset,
36830             i;
36831         if (dirOffset + 6 > dataView.byteLength) {
36832             Roo.log('Invalid Exif data: Invalid directory offset.');
36833             return;
36834         }
36835         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36836         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36837         if (dirEndOffset + 4 > dataView.byteLength) {
36838             Roo.log('Invalid Exif data: Invalid directory size.');
36839             return;
36840         }
36841         for (i = 0; i < tagsNumber; i += 1) {
36842             this.parseExifTag(
36843                 dataView,
36844                 tiffOffset,
36845                 dirOffset + 2 + 12 * i, // tag offset
36846                 littleEndian
36847             );
36848         }
36849         // Return the offset to the next directory:
36850         return dataView.getUint32(dirEndOffset, littleEndian);
36851     },
36852     
36853     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
36854     {
36855         var tag = dataView.getUint16(offset, littleEndian);
36856         
36857         this.exif[tag] = this.getExifValue(
36858             dataView,
36859             tiffOffset,
36860             offset,
36861             dataView.getUint16(offset + 2, littleEndian), // tag type
36862             dataView.getUint32(offset + 4, littleEndian), // tag length
36863             littleEndian
36864         );
36865     },
36866     
36867     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36868     {
36869         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36870             tagSize,
36871             dataOffset,
36872             values,
36873             i,
36874             str,
36875             c;
36876     
36877         if (!tagType) {
36878             Roo.log('Invalid Exif data: Invalid tag type.');
36879             return;
36880         }
36881         
36882         tagSize = tagType.size * length;
36883         // Determine if the value is contained in the dataOffset bytes,
36884         // or if the value at the dataOffset is a pointer to the actual data:
36885         dataOffset = tagSize > 4 ?
36886                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36887         if (dataOffset + tagSize > dataView.byteLength) {
36888             Roo.log('Invalid Exif data: Invalid data offset.');
36889             return;
36890         }
36891         if (length === 1) {
36892             return tagType.getValue(dataView, dataOffset, littleEndian);
36893         }
36894         values = [];
36895         for (i = 0; i < length; i += 1) {
36896             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36897         }
36898         
36899         if (tagType.ascii) {
36900             str = '';
36901             // Concatenate the chars:
36902             for (i = 0; i < values.length; i += 1) {
36903                 c = values[i];
36904                 // Ignore the terminating NULL byte(s):
36905                 if (c === '\u0000') {
36906                     break;
36907                 }
36908                 str += c;
36909             }
36910             return str;
36911         }
36912         return values;
36913     }
36914     
36915 });
36916
36917 Roo.apply(Roo.bootstrap.UploadCropbox, {
36918     tags : {
36919         'Orientation': 0x0112
36920     },
36921     
36922     Orientation: {
36923             1: 0, //'top-left',
36924 //            2: 'top-right',
36925             3: 180, //'bottom-right',
36926 //            4: 'bottom-left',
36927 //            5: 'left-top',
36928             6: 90, //'right-top',
36929 //            7: 'right-bottom',
36930             8: 270 //'left-bottom'
36931     },
36932     
36933     exifTagTypes : {
36934         // byte, 8-bit unsigned int:
36935         1: {
36936             getValue: function (dataView, dataOffset) {
36937                 return dataView.getUint8(dataOffset);
36938             },
36939             size: 1
36940         },
36941         // ascii, 8-bit byte:
36942         2: {
36943             getValue: function (dataView, dataOffset) {
36944                 return String.fromCharCode(dataView.getUint8(dataOffset));
36945             },
36946             size: 1,
36947             ascii: true
36948         },
36949         // short, 16 bit int:
36950         3: {
36951             getValue: function (dataView, dataOffset, littleEndian) {
36952                 return dataView.getUint16(dataOffset, littleEndian);
36953             },
36954             size: 2
36955         },
36956         // long, 32 bit int:
36957         4: {
36958             getValue: function (dataView, dataOffset, littleEndian) {
36959                 return dataView.getUint32(dataOffset, littleEndian);
36960             },
36961             size: 4
36962         },
36963         // rational = two long values, first is numerator, second is denominator:
36964         5: {
36965             getValue: function (dataView, dataOffset, littleEndian) {
36966                 return dataView.getUint32(dataOffset, littleEndian) /
36967                     dataView.getUint32(dataOffset + 4, littleEndian);
36968             },
36969             size: 8
36970         },
36971         // slong, 32 bit signed int:
36972         9: {
36973             getValue: function (dataView, dataOffset, littleEndian) {
36974                 return dataView.getInt32(dataOffset, littleEndian);
36975             },
36976             size: 4
36977         },
36978         // srational, two slongs, first is numerator, second is denominator:
36979         10: {
36980             getValue: function (dataView, dataOffset, littleEndian) {
36981                 return dataView.getInt32(dataOffset, littleEndian) /
36982                     dataView.getInt32(dataOffset + 4, littleEndian);
36983             },
36984             size: 8
36985         }
36986     },
36987     
36988     footer : {
36989         STANDARD : [
36990             {
36991                 tag : 'div',
36992                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36993                 action : 'rotate-left',
36994                 cn : [
36995                     {
36996                         tag : 'button',
36997                         cls : 'btn btn-default',
36998                         html : '<i class="fa fa-undo"></i>'
36999                     }
37000                 ]
37001             },
37002             {
37003                 tag : 'div',
37004                 cls : 'btn-group roo-upload-cropbox-picture',
37005                 action : 'picture',
37006                 cn : [
37007                     {
37008                         tag : 'button',
37009                         cls : 'btn btn-default',
37010                         html : '<i class="fa fa-picture-o"></i>'
37011                     }
37012                 ]
37013             },
37014             {
37015                 tag : 'div',
37016                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37017                 action : 'rotate-right',
37018                 cn : [
37019                     {
37020                         tag : 'button',
37021                         cls : 'btn btn-default',
37022                         html : '<i class="fa fa-repeat"></i>'
37023                     }
37024                 ]
37025             }
37026         ],
37027         DOCUMENT : [
37028             {
37029                 tag : 'div',
37030                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37031                 action : 'rotate-left',
37032                 cn : [
37033                     {
37034                         tag : 'button',
37035                         cls : 'btn btn-default',
37036                         html : '<i class="fa fa-undo"></i>'
37037                     }
37038                 ]
37039             },
37040             {
37041                 tag : 'div',
37042                 cls : 'btn-group roo-upload-cropbox-download',
37043                 action : 'download',
37044                 cn : [
37045                     {
37046                         tag : 'button',
37047                         cls : 'btn btn-default',
37048                         html : '<i class="fa fa-download"></i>'
37049                     }
37050                 ]
37051             },
37052             {
37053                 tag : 'div',
37054                 cls : 'btn-group roo-upload-cropbox-crop',
37055                 action : 'crop',
37056                 cn : [
37057                     {
37058                         tag : 'button',
37059                         cls : 'btn btn-default',
37060                         html : '<i class="fa fa-crop"></i>'
37061                     }
37062                 ]
37063             },
37064             {
37065                 tag : 'div',
37066                 cls : 'btn-group roo-upload-cropbox-trash',
37067                 action : 'trash',
37068                 cn : [
37069                     {
37070                         tag : 'button',
37071                         cls : 'btn btn-default',
37072                         html : '<i class="fa fa-trash"></i>'
37073                     }
37074                 ]
37075             },
37076             {
37077                 tag : 'div',
37078                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37079                 action : 'rotate-right',
37080                 cn : [
37081                     {
37082                         tag : 'button',
37083                         cls : 'btn btn-default',
37084                         html : '<i class="fa fa-repeat"></i>'
37085                     }
37086                 ]
37087             }
37088         ],
37089         ROTATOR : [
37090             {
37091                 tag : 'div',
37092                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37093                 action : 'rotate-left',
37094                 cn : [
37095                     {
37096                         tag : 'button',
37097                         cls : 'btn btn-default',
37098                         html : '<i class="fa fa-undo"></i>'
37099                     }
37100                 ]
37101             },
37102             {
37103                 tag : 'div',
37104                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37105                 action : 'rotate-right',
37106                 cn : [
37107                     {
37108                         tag : 'button',
37109                         cls : 'btn btn-default',
37110                         html : '<i class="fa fa-repeat"></i>'
37111                     }
37112                 ]
37113             }
37114         ]
37115     }
37116 });
37117
37118 /*
37119 * Licence: LGPL
37120 */
37121
37122 /**
37123  * @class Roo.bootstrap.DocumentManager
37124  * @extends Roo.bootstrap.Component
37125  * Bootstrap DocumentManager class
37126  * @cfg {String} paramName default 'imageUpload'
37127  * @cfg {String} toolTipName default 'filename'
37128  * @cfg {String} method default POST
37129  * @cfg {String} url action url
37130  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37131  * @cfg {Boolean} multiple multiple upload default true
37132  * @cfg {Number} thumbSize default 300
37133  * @cfg {String} fieldLabel
37134  * @cfg {Number} labelWidth default 4
37135  * @cfg {String} labelAlign (left|top) default left
37136  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37137 * @cfg {Number} labellg set the width of label (1-12)
37138  * @cfg {Number} labelmd set the width of label (1-12)
37139  * @cfg {Number} labelsm set the width of label (1-12)
37140  * @cfg {Number} labelxs set the width of label (1-12)
37141  * 
37142  * @constructor
37143  * Create a new DocumentManager
37144  * @param {Object} config The config object
37145  */
37146
37147 Roo.bootstrap.DocumentManager = function(config){
37148     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37149     
37150     this.files = [];
37151     this.delegates = [];
37152     
37153     this.addEvents({
37154         /**
37155          * @event initial
37156          * Fire when initial the DocumentManager
37157          * @param {Roo.bootstrap.DocumentManager} this
37158          */
37159         "initial" : true,
37160         /**
37161          * @event inspect
37162          * inspect selected file
37163          * @param {Roo.bootstrap.DocumentManager} this
37164          * @param {File} file
37165          */
37166         "inspect" : true,
37167         /**
37168          * @event exception
37169          * Fire when xhr load exception
37170          * @param {Roo.bootstrap.DocumentManager} this
37171          * @param {XMLHttpRequest} xhr
37172          */
37173         "exception" : true,
37174         /**
37175          * @event afterupload
37176          * Fire when xhr load exception
37177          * @param {Roo.bootstrap.DocumentManager} this
37178          * @param {XMLHttpRequest} xhr
37179          */
37180         "afterupload" : true,
37181         /**
37182          * @event prepare
37183          * prepare the form data
37184          * @param {Roo.bootstrap.DocumentManager} this
37185          * @param {Object} formData
37186          */
37187         "prepare" : true,
37188         /**
37189          * @event remove
37190          * Fire when remove the file
37191          * @param {Roo.bootstrap.DocumentManager} this
37192          * @param {Object} file
37193          */
37194         "remove" : true,
37195         /**
37196          * @event refresh
37197          * Fire after refresh the file
37198          * @param {Roo.bootstrap.DocumentManager} this
37199          */
37200         "refresh" : true,
37201         /**
37202          * @event click
37203          * Fire after click the image
37204          * @param {Roo.bootstrap.DocumentManager} this
37205          * @param {Object} file
37206          */
37207         "click" : true,
37208         /**
37209          * @event edit
37210          * Fire when upload a image and editable set to true
37211          * @param {Roo.bootstrap.DocumentManager} this
37212          * @param {Object} file
37213          */
37214         "edit" : true,
37215         /**
37216          * @event beforeselectfile
37217          * Fire before select file
37218          * @param {Roo.bootstrap.DocumentManager} this
37219          */
37220         "beforeselectfile" : true,
37221         /**
37222          * @event process
37223          * Fire before process file
37224          * @param {Roo.bootstrap.DocumentManager} this
37225          * @param {Object} file
37226          */
37227         "process" : true,
37228         /**
37229          * @event previewrendered
37230          * Fire when preview rendered
37231          * @param {Roo.bootstrap.DocumentManager} this
37232          * @param {Object} file
37233          */
37234         "previewrendered" : true,
37235         /**
37236          */
37237         "previewResize" : true
37238         
37239     });
37240 };
37241
37242 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
37243     
37244     boxes : 0,
37245     inputName : '',
37246     thumbSize : 300,
37247     multiple : true,
37248     files : false,
37249     method : 'POST',
37250     url : '',
37251     paramName : 'imageUpload',
37252     toolTipName : 'filename',
37253     fieldLabel : '',
37254     labelWidth : 4,
37255     labelAlign : 'left',
37256     editable : true,
37257     delegates : false,
37258     xhr : false, 
37259     
37260     labellg : 0,
37261     labelmd : 0,
37262     labelsm : 0,
37263     labelxs : 0,
37264     
37265     getAutoCreate : function()
37266     {   
37267         var managerWidget = {
37268             tag : 'div',
37269             cls : 'roo-document-manager',
37270             cn : [
37271                 {
37272                     tag : 'input',
37273                     cls : 'roo-document-manager-selector',
37274                     type : 'file'
37275                 },
37276                 {
37277                     tag : 'div',
37278                     cls : 'roo-document-manager-uploader',
37279                     cn : [
37280                         {
37281                             tag : 'div',
37282                             cls : 'roo-document-manager-upload-btn',
37283                             html : '<i class="fa fa-plus"></i>'
37284                         }
37285                     ]
37286                     
37287                 }
37288             ]
37289         };
37290         
37291         var content = [
37292             {
37293                 tag : 'div',
37294                 cls : 'column col-md-12',
37295                 cn : managerWidget
37296             }
37297         ];
37298         
37299         if(this.fieldLabel.length){
37300             
37301             content = [
37302                 {
37303                     tag : 'div',
37304                     cls : 'column col-md-12',
37305                     html : this.fieldLabel
37306                 },
37307                 {
37308                     tag : 'div',
37309                     cls : 'column col-md-12',
37310                     cn : managerWidget
37311                 }
37312             ];
37313
37314             if(this.labelAlign == 'left'){
37315                 content = [
37316                     {
37317                         tag : 'div',
37318                         cls : 'column',
37319                         html : this.fieldLabel
37320                     },
37321                     {
37322                         tag : 'div',
37323                         cls : 'column',
37324                         cn : managerWidget
37325                     }
37326                 ];
37327                 
37328                 if(this.labelWidth > 12){
37329                     content[0].style = "width: " + this.labelWidth + 'px';
37330                 }
37331
37332                 if(this.labelWidth < 13 && this.labelmd == 0){
37333                     this.labelmd = this.labelWidth;
37334                 }
37335
37336                 if(this.labellg > 0){
37337                     content[0].cls += ' col-lg-' + this.labellg;
37338                     content[1].cls += ' col-lg-' + (12 - this.labellg);
37339                 }
37340
37341                 if(this.labelmd > 0){
37342                     content[0].cls += ' col-md-' + this.labelmd;
37343                     content[1].cls += ' col-md-' + (12 - this.labelmd);
37344                 }
37345
37346                 if(this.labelsm > 0){
37347                     content[0].cls += ' col-sm-' + this.labelsm;
37348                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
37349                 }
37350
37351                 if(this.labelxs > 0){
37352                     content[0].cls += ' col-xs-' + this.labelxs;
37353                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
37354                 }
37355                 
37356             }
37357         }
37358         
37359         var cfg = {
37360             tag : 'div',
37361             cls : 'row clearfix',
37362             cn : content
37363         };
37364         
37365         return cfg;
37366         
37367     },
37368     
37369     initEvents : function()
37370     {
37371         this.managerEl = this.el.select('.roo-document-manager', true).first();
37372         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37373         
37374         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37375         this.selectorEl.hide();
37376         
37377         if(this.multiple){
37378             this.selectorEl.attr('multiple', 'multiple');
37379         }
37380         
37381         this.selectorEl.on('change', this.onFileSelected, this);
37382         
37383         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37384         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37385         
37386         this.uploader.on('click', this.onUploaderClick, this);
37387         
37388         this.renderProgressDialog();
37389         
37390         var _this = this;
37391         
37392         window.addEventListener("resize", function() { _this.refresh(); } );
37393         
37394         this.fireEvent('initial', this);
37395     },
37396     
37397     renderProgressDialog : function()
37398     {
37399         var _this = this;
37400         
37401         this.progressDialog = new Roo.bootstrap.Modal({
37402             cls : 'roo-document-manager-progress-dialog',
37403             allow_close : false,
37404             animate : false,
37405             title : '',
37406             buttons : [
37407                 {
37408                     name  :'cancel',
37409                     weight : 'danger',
37410                     html : 'Cancel'
37411                 }
37412             ], 
37413             listeners : { 
37414                 btnclick : function() {
37415                     _this.uploadCancel();
37416                     this.hide();
37417                 }
37418             }
37419         });
37420          
37421         this.progressDialog.render(Roo.get(document.body));
37422          
37423         this.progress = new Roo.bootstrap.Progress({
37424             cls : 'roo-document-manager-progress',
37425             active : true,
37426             striped : true
37427         });
37428         
37429         this.progress.render(this.progressDialog.getChildContainer());
37430         
37431         this.progressBar = new Roo.bootstrap.ProgressBar({
37432             cls : 'roo-document-manager-progress-bar',
37433             aria_valuenow : 0,
37434             aria_valuemin : 0,
37435             aria_valuemax : 12,
37436             panel : 'success'
37437         });
37438         
37439         this.progressBar.render(this.progress.getChildContainer());
37440     },
37441     
37442     onUploaderClick : function(e)
37443     {
37444         e.preventDefault();
37445      
37446         if(this.fireEvent('beforeselectfile', this) != false){
37447             this.selectorEl.dom.click();
37448         }
37449         
37450     },
37451     
37452     onFileSelected : function(e)
37453     {
37454         e.preventDefault();
37455         
37456         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37457             return;
37458         }
37459         
37460         Roo.each(this.selectorEl.dom.files, function(file){
37461             if(this.fireEvent('inspect', this, file) != false){
37462                 this.files.push(file);
37463             }
37464         }, this);
37465         
37466         this.queue();
37467         
37468     },
37469     
37470     queue : function()
37471     {
37472         this.selectorEl.dom.value = '';
37473         
37474         if(!this.files || !this.files.length){
37475             return;
37476         }
37477         
37478         if(this.boxes > 0 && this.files.length > this.boxes){
37479             this.files = this.files.slice(0, this.boxes);
37480         }
37481         
37482         this.uploader.show();
37483         
37484         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37485             this.uploader.hide();
37486         }
37487         
37488         var _this = this;
37489         
37490         var files = [];
37491         
37492         var docs = [];
37493         
37494         Roo.each(this.files, function(file){
37495             
37496             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37497                 var f = this.renderPreview(file);
37498                 files.push(f);
37499                 return;
37500             }
37501             
37502             if(file.type.indexOf('image') != -1){
37503                 this.delegates.push(
37504                     (function(){
37505                         _this.process(file);
37506                     }).createDelegate(this)
37507                 );
37508         
37509                 return;
37510             }
37511             
37512             docs.push(
37513                 (function(){
37514                     _this.process(file);
37515                 }).createDelegate(this)
37516             );
37517             
37518         }, this);
37519         
37520         this.files = files;
37521         
37522         this.delegates = this.delegates.concat(docs);
37523         
37524         if(!this.delegates.length){
37525             this.refresh();
37526             return;
37527         }
37528         
37529         this.progressBar.aria_valuemax = this.delegates.length;
37530         
37531         this.arrange();
37532         
37533         return;
37534     },
37535     
37536     arrange : function()
37537     {
37538         if(!this.delegates.length){
37539             this.progressDialog.hide();
37540             this.refresh();
37541             return;
37542         }
37543         
37544         var delegate = this.delegates.shift();
37545         
37546         this.progressDialog.show();
37547         
37548         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37549         
37550         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37551         
37552         delegate();
37553     },
37554     
37555     refresh : function()
37556     {
37557         this.uploader.show();
37558         
37559         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37560             this.uploader.hide();
37561         }
37562         
37563         Roo.isTouch ? this.closable(false) : this.closable(true);
37564         
37565         this.fireEvent('refresh', this);
37566     },
37567     
37568     onRemove : function(e, el, o)
37569     {
37570         e.preventDefault();
37571         
37572         this.fireEvent('remove', this, o);
37573         
37574     },
37575     
37576     remove : function(o)
37577     {
37578         var files = [];
37579         
37580         Roo.each(this.files, function(file){
37581             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37582                 files.push(file);
37583                 return;
37584             }
37585
37586             o.target.remove();
37587
37588         }, this);
37589         
37590         this.files = files;
37591         
37592         this.refresh();
37593     },
37594     
37595     clear : function()
37596     {
37597         Roo.each(this.files, function(file){
37598             if(!file.target){
37599                 return;
37600             }
37601             
37602             file.target.remove();
37603
37604         }, this);
37605         
37606         this.files = [];
37607         
37608         this.refresh();
37609     },
37610     
37611     onClick : function(e, el, o)
37612     {
37613         e.preventDefault();
37614         
37615         this.fireEvent('click', this, o);
37616         
37617     },
37618     
37619     closable : function(closable)
37620     {
37621         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37622             
37623             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37624             
37625             if(closable){
37626                 el.show();
37627                 return;
37628             }
37629             
37630             el.hide();
37631             
37632         }, this);
37633     },
37634     
37635     xhrOnLoad : function(xhr)
37636     {
37637         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37638             el.remove();
37639         }, this);
37640         
37641         if (xhr.readyState !== 4) {
37642             this.arrange();
37643             this.fireEvent('exception', this, xhr);
37644             return;
37645         }
37646
37647         var response = Roo.decode(xhr.responseText);
37648         
37649         if(!response.success){
37650             this.arrange();
37651             this.fireEvent('exception', this, xhr);
37652             return;
37653         }
37654         
37655         var file = this.renderPreview(response.data);
37656         
37657         this.files.push(file);
37658         
37659         this.arrange();
37660         
37661         this.fireEvent('afterupload', this, xhr);
37662         
37663     },
37664     
37665     xhrOnError : function(xhr)
37666     {
37667         Roo.log('xhr on error');
37668         
37669         var response = Roo.decode(xhr.responseText);
37670           
37671         Roo.log(response);
37672         
37673         this.arrange();
37674     },
37675     
37676     process : function(file)
37677     {
37678         if(this.fireEvent('process', this, file) !== false){
37679             if(this.editable && file.type.indexOf('image') != -1){
37680                 this.fireEvent('edit', this, file);
37681                 return;
37682             }
37683
37684             this.uploadStart(file, false);
37685
37686             return;
37687         }
37688         
37689     },
37690     
37691     uploadStart : function(file, crop)
37692     {
37693         this.xhr = new XMLHttpRequest();
37694         
37695         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37696             this.arrange();
37697             return;
37698         }
37699         
37700         file.xhr = this.xhr;
37701             
37702         this.managerEl.createChild({
37703             tag : 'div',
37704             cls : 'roo-document-manager-loading',
37705             cn : [
37706                 {
37707                     tag : 'div',
37708                     tooltip : file.name,
37709                     cls : 'roo-document-manager-thumb',
37710                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37711                 }
37712             ]
37713
37714         });
37715
37716         this.xhr.open(this.method, this.url, true);
37717         
37718         var headers = {
37719             "Accept": "application/json",
37720             "Cache-Control": "no-cache",
37721             "X-Requested-With": "XMLHttpRequest"
37722         };
37723         
37724         for (var headerName in headers) {
37725             var headerValue = headers[headerName];
37726             if (headerValue) {
37727                 this.xhr.setRequestHeader(headerName, headerValue);
37728             }
37729         }
37730         
37731         var _this = this;
37732         
37733         this.xhr.onload = function()
37734         {
37735             _this.xhrOnLoad(_this.xhr);
37736         }
37737         
37738         this.xhr.onerror = function()
37739         {
37740             _this.xhrOnError(_this.xhr);
37741         }
37742         
37743         var formData = new FormData();
37744
37745         formData.append('returnHTML', 'NO');
37746         
37747         if(crop){
37748             formData.append('crop', crop);
37749         }
37750         
37751         formData.append(this.paramName, file, file.name);
37752         
37753         var options = {
37754             file : file, 
37755             manually : false
37756         };
37757         
37758         if(this.fireEvent('prepare', this, formData, options) != false){
37759             
37760             if(options.manually){
37761                 return;
37762             }
37763             
37764             this.xhr.send(formData);
37765             return;
37766         };
37767         
37768         this.uploadCancel();
37769     },
37770     
37771     uploadCancel : function()
37772     {
37773         if (this.xhr) {
37774             this.xhr.abort();
37775         }
37776         
37777         this.delegates = [];
37778         
37779         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37780             el.remove();
37781         }, this);
37782         
37783         this.arrange();
37784     },
37785     
37786     renderPreview : function(file)
37787     {
37788         if(typeof(file.target) != 'undefined' && file.target){
37789             return file;
37790         }
37791         
37792         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37793         
37794         var previewEl = this.managerEl.createChild({
37795             tag : 'div',
37796             cls : 'roo-document-manager-preview',
37797             cn : [
37798                 {
37799                     tag : 'div',
37800                     tooltip : file[this.toolTipName],
37801                     cls : 'roo-document-manager-thumb',
37802                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37803                 },
37804                 {
37805                     tag : 'button',
37806                     cls : 'close',
37807                     html : '<i class="fa fa-times-circle"></i>'
37808                 }
37809             ]
37810         });
37811
37812         var close = previewEl.select('button.close', true).first();
37813
37814         close.on('click', this.onRemove, this, file);
37815
37816         file.target = previewEl;
37817
37818         var image = previewEl.select('img', true).first();
37819         
37820         var _this = this;
37821         
37822         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37823         
37824         image.on('click', this.onClick, this, file);
37825         
37826         this.fireEvent('previewrendered', this, file);
37827         
37828         return file;
37829         
37830     },
37831     
37832     onPreviewLoad : function(file, image)
37833     {
37834         if(typeof(file.target) == 'undefined' || !file.target){
37835             return;
37836         }
37837         
37838         var width = image.dom.naturalWidth || image.dom.width;
37839         var height = image.dom.naturalHeight || image.dom.height;
37840         
37841         if(!this.previewResize) {
37842             return;
37843         }
37844         
37845         if(width > height){
37846             file.target.addClass('wide');
37847             return;
37848         }
37849         
37850         file.target.addClass('tall');
37851         return;
37852         
37853     },
37854     
37855     uploadFromSource : function(file, crop)
37856     {
37857         this.xhr = new XMLHttpRequest();
37858         
37859         this.managerEl.createChild({
37860             tag : 'div',
37861             cls : 'roo-document-manager-loading',
37862             cn : [
37863                 {
37864                     tag : 'div',
37865                     tooltip : file.name,
37866                     cls : 'roo-document-manager-thumb',
37867                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37868                 }
37869             ]
37870
37871         });
37872
37873         this.xhr.open(this.method, this.url, true);
37874         
37875         var headers = {
37876             "Accept": "application/json",
37877             "Cache-Control": "no-cache",
37878             "X-Requested-With": "XMLHttpRequest"
37879         };
37880         
37881         for (var headerName in headers) {
37882             var headerValue = headers[headerName];
37883             if (headerValue) {
37884                 this.xhr.setRequestHeader(headerName, headerValue);
37885             }
37886         }
37887         
37888         var _this = this;
37889         
37890         this.xhr.onload = function()
37891         {
37892             _this.xhrOnLoad(_this.xhr);
37893         }
37894         
37895         this.xhr.onerror = function()
37896         {
37897             _this.xhrOnError(_this.xhr);
37898         }
37899         
37900         var formData = new FormData();
37901
37902         formData.append('returnHTML', 'NO');
37903         
37904         formData.append('crop', crop);
37905         
37906         if(typeof(file.filename) != 'undefined'){
37907             formData.append('filename', file.filename);
37908         }
37909         
37910         if(typeof(file.mimetype) != 'undefined'){
37911             formData.append('mimetype', file.mimetype);
37912         }
37913         
37914         Roo.log(formData);
37915         
37916         if(this.fireEvent('prepare', this, formData) != false){
37917             this.xhr.send(formData);
37918         };
37919     }
37920 });
37921
37922 /*
37923 * Licence: LGPL
37924 */
37925
37926 /**
37927  * @class Roo.bootstrap.DocumentViewer
37928  * @extends Roo.bootstrap.Component
37929  * Bootstrap DocumentViewer class
37930  * @cfg {Boolean} showDownload (true|false) show download button (default true)
37931  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37932  * 
37933  * @constructor
37934  * Create a new DocumentViewer
37935  * @param {Object} config The config object
37936  */
37937
37938 Roo.bootstrap.DocumentViewer = function(config){
37939     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37940     
37941     this.addEvents({
37942         /**
37943          * @event initial
37944          * Fire after initEvent
37945          * @param {Roo.bootstrap.DocumentViewer} this
37946          */
37947         "initial" : true,
37948         /**
37949          * @event click
37950          * Fire after click
37951          * @param {Roo.bootstrap.DocumentViewer} this
37952          */
37953         "click" : true,
37954         /**
37955          * @event download
37956          * Fire after download button
37957          * @param {Roo.bootstrap.DocumentViewer} this
37958          */
37959         "download" : true,
37960         /**
37961          * @event trash
37962          * Fire after trash button
37963          * @param {Roo.bootstrap.DocumentViewer} this
37964          */
37965         "trash" : true
37966         
37967     });
37968 };
37969
37970 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
37971     
37972     showDownload : true,
37973     
37974     showTrash : true,
37975     
37976     getAutoCreate : function()
37977     {
37978         var cfg = {
37979             tag : 'div',
37980             cls : 'roo-document-viewer',
37981             cn : [
37982                 {
37983                     tag : 'div',
37984                     cls : 'roo-document-viewer-body',
37985                     cn : [
37986                         {
37987                             tag : 'div',
37988                             cls : 'roo-document-viewer-thumb',
37989                             cn : [
37990                                 {
37991                                     tag : 'img',
37992                                     cls : 'roo-document-viewer-image'
37993                                 }
37994                             ]
37995                         }
37996                     ]
37997                 },
37998                 {
37999                     tag : 'div',
38000                     cls : 'roo-document-viewer-footer',
38001                     cn : {
38002                         tag : 'div',
38003                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
38004                         cn : [
38005                             {
38006                                 tag : 'div',
38007                                 cls : 'btn-group roo-document-viewer-download',
38008                                 cn : [
38009                                     {
38010                                         tag : 'button',
38011                                         cls : 'btn btn-default',
38012                                         html : '<i class="fa fa-download"></i>'
38013                                     }
38014                                 ]
38015                             },
38016                             {
38017                                 tag : 'div',
38018                                 cls : 'btn-group roo-document-viewer-trash',
38019                                 cn : [
38020                                     {
38021                                         tag : 'button',
38022                                         cls : 'btn btn-default',
38023                                         html : '<i class="fa fa-trash"></i>'
38024                                     }
38025                                 ]
38026                             }
38027                         ]
38028                     }
38029                 }
38030             ]
38031         };
38032         
38033         return cfg;
38034     },
38035     
38036     initEvents : function()
38037     {
38038         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
38039         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
38040         
38041         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
38042         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
38043         
38044         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
38045         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
38046         
38047         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
38048         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
38049         
38050         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
38051         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
38052         
38053         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
38054         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38055         
38056         this.bodyEl.on('click', this.onClick, this);
38057         this.downloadBtn.on('click', this.onDownload, this);
38058         this.trashBtn.on('click', this.onTrash, this);
38059         
38060         this.downloadBtn.hide();
38061         this.trashBtn.hide();
38062         
38063         if(this.showDownload){
38064             this.downloadBtn.show();
38065         }
38066         
38067         if(this.showTrash){
38068             this.trashBtn.show();
38069         }
38070         
38071         if(!this.showDownload && !this.showTrash) {
38072             this.footerEl.hide();
38073         }
38074         
38075     },
38076     
38077     initial : function()
38078     {
38079         this.fireEvent('initial', this);
38080         
38081     },
38082     
38083     onClick : function(e)
38084     {
38085         e.preventDefault();
38086         
38087         this.fireEvent('click', this);
38088     },
38089     
38090     onDownload : function(e)
38091     {
38092         e.preventDefault();
38093         
38094         this.fireEvent('download', this);
38095     },
38096     
38097     onTrash : function(e)
38098     {
38099         e.preventDefault();
38100         
38101         this.fireEvent('trash', this);
38102     }
38103     
38104 });
38105 /*
38106  * - LGPL
38107  *
38108  * FieldLabel
38109  * 
38110  */
38111
38112 /**
38113  * @class Roo.bootstrap.form.FieldLabel
38114  * @extends Roo.bootstrap.Component
38115  * Bootstrap FieldLabel class
38116  * @cfg {String} html contents of the element
38117  * @cfg {String} tag tag of the element default label
38118  * @cfg {String} cls class of the element
38119  * @cfg {String} target label target 
38120  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38121  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38122  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38123  * @cfg {String} iconTooltip default "This field is required"
38124  * @cfg {String} indicatorpos (left|right) default left
38125  * 
38126  * @constructor
38127  * Create a new FieldLabel
38128  * @param {Object} config The config object
38129  */
38130
38131 Roo.bootstrap.form.FieldLabel = function(config){
38132     Roo.bootstrap.Element.superclass.constructor.call(this, config);
38133     
38134     this.addEvents({
38135             /**
38136              * @event invalid
38137              * Fires after the field has been marked as invalid.
38138              * @param {Roo.form.FieldLabel} this
38139              * @param {String} msg The validation message
38140              */
38141             invalid : true,
38142             /**
38143              * @event valid
38144              * Fires after the field has been validated with no errors.
38145              * @param {Roo.form.FieldLabel} this
38146              */
38147             valid : true
38148         });
38149 };
38150
38151 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
38152     
38153     tag: 'label',
38154     cls: '',
38155     html: '',
38156     target: '',
38157     allowBlank : true,
38158     invalidClass : 'has-warning',
38159     validClass : 'has-success',
38160     iconTooltip : 'This field is required',
38161     indicatorpos : 'left',
38162     
38163     getAutoCreate : function(){
38164         
38165         var cls = "";
38166         if (!this.allowBlank) {
38167             cls  = "visible";
38168         }
38169         
38170         var cfg = {
38171             tag : this.tag,
38172             cls : 'roo-bootstrap-field-label ' + this.cls,
38173             for : this.target,
38174             cn : [
38175                 {
38176                     tag : 'i',
38177                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38178                     tooltip : this.iconTooltip
38179                 },
38180                 {
38181                     tag : 'span',
38182                     html : this.html
38183                 }
38184             ] 
38185         };
38186         
38187         if(this.indicatorpos == 'right'){
38188             var cfg = {
38189                 tag : this.tag,
38190                 cls : 'roo-bootstrap-field-label ' + this.cls,
38191                 for : this.target,
38192                 cn : [
38193                     {
38194                         tag : 'span',
38195                         html : this.html
38196                     },
38197                     {
38198                         tag : 'i',
38199                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38200                         tooltip : this.iconTooltip
38201                     }
38202                 ] 
38203             };
38204         }
38205         
38206         return cfg;
38207     },
38208     
38209     initEvents: function() 
38210     {
38211         Roo.bootstrap.Element.superclass.initEvents.call(this);
38212         
38213         this.indicator = this.indicatorEl();
38214         
38215         if(this.indicator){
38216             this.indicator.removeClass('visible');
38217             this.indicator.addClass('invisible');
38218         }
38219         
38220         Roo.bootstrap.form.FieldLabel.register(this);
38221     },
38222     
38223     indicatorEl : function()
38224     {
38225         var indicator = this.el.select('i.roo-required-indicator',true).first();
38226         
38227         if(!indicator){
38228             return false;
38229         }
38230         
38231         return indicator;
38232         
38233     },
38234     
38235     /**
38236      * Mark this field as valid
38237      */
38238     markValid : function()
38239     {
38240         if(this.indicator){
38241             this.indicator.removeClass('visible');
38242             this.indicator.addClass('invisible');
38243         }
38244         if (Roo.bootstrap.version == 3) {
38245             this.el.removeClass(this.invalidClass);
38246             this.el.addClass(this.validClass);
38247         } else {
38248             this.el.removeClass('is-invalid');
38249             this.el.addClass('is-valid');
38250         }
38251         
38252         
38253         this.fireEvent('valid', this);
38254     },
38255     
38256     /**
38257      * Mark this field as invalid
38258      * @param {String} msg The validation message
38259      */
38260     markInvalid : function(msg)
38261     {
38262         if(this.indicator){
38263             this.indicator.removeClass('invisible');
38264             this.indicator.addClass('visible');
38265         }
38266           if (Roo.bootstrap.version == 3) {
38267             this.el.removeClass(this.validClass);
38268             this.el.addClass(this.invalidClass);
38269         } else {
38270             this.el.removeClass('is-valid');
38271             this.el.addClass('is-invalid');
38272         }
38273         
38274         
38275         this.fireEvent('invalid', this, msg);
38276     }
38277     
38278    
38279 });
38280
38281 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38282     
38283     groups: {},
38284     
38285      /**
38286     * register a FieldLabel Group
38287     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38288     */
38289     register : function(label)
38290     {
38291         if(this.groups.hasOwnProperty(label.target)){
38292             return;
38293         }
38294      
38295         this.groups[label.target] = label;
38296         
38297     },
38298     /**
38299     * fetch a FieldLabel Group based on the target
38300     * @param {string} target
38301     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38302     */
38303     get: function(target) {
38304         if (typeof(this.groups[target]) == 'undefined') {
38305             return false;
38306         }
38307         
38308         return this.groups[target] ;
38309     }
38310 });
38311
38312  
38313
38314  /*
38315  * - LGPL
38316  *
38317  * page DateSplitField.
38318  * 
38319  */
38320
38321
38322 /**
38323  * @class Roo.bootstrap.form.DateSplitField
38324  * @extends Roo.bootstrap.Component
38325  * Bootstrap DateSplitField class
38326  * @cfg {string} fieldLabel - the label associated
38327  * @cfg {Number} labelWidth set the width of label (0-12)
38328  * @cfg {String} labelAlign (top|left)
38329  * @cfg {Boolean} dayAllowBlank (true|false) default false
38330  * @cfg {Boolean} monthAllowBlank (true|false) default false
38331  * @cfg {Boolean} yearAllowBlank (true|false) default false
38332  * @cfg {string} dayPlaceholder 
38333  * @cfg {string} monthPlaceholder
38334  * @cfg {string} yearPlaceholder
38335  * @cfg {string} dayFormat default 'd'
38336  * @cfg {string} monthFormat default 'm'
38337  * @cfg {string} yearFormat default 'Y'
38338  * @cfg {Number} labellg set the width of label (1-12)
38339  * @cfg {Number} labelmd set the width of label (1-12)
38340  * @cfg {Number} labelsm set the width of label (1-12)
38341  * @cfg {Number} labelxs set the width of label (1-12)
38342
38343  *     
38344  * @constructor
38345  * Create a new DateSplitField
38346  * @param {Object} config The config object
38347  */
38348
38349 Roo.bootstrap.form.DateSplitField = function(config){
38350     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38351     
38352     this.addEvents({
38353         // raw events
38354          /**
38355          * @event years
38356          * getting the data of years
38357          * @param {Roo.bootstrap.form.DateSplitField} this
38358          * @param {Object} years
38359          */
38360         "years" : true,
38361         /**
38362          * @event days
38363          * getting the data of days
38364          * @param {Roo.bootstrap.form.DateSplitField} this
38365          * @param {Object} days
38366          */
38367         "days" : true,
38368         /**
38369          * @event invalid
38370          * Fires after the field has been marked as invalid.
38371          * @param {Roo.form.Field} this
38372          * @param {String} msg The validation message
38373          */
38374         invalid : true,
38375        /**
38376          * @event valid
38377          * Fires after the field has been validated with no errors.
38378          * @param {Roo.form.Field} this
38379          */
38380         valid : true
38381     });
38382 };
38383
38384 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
38385     
38386     fieldLabel : '',
38387     labelAlign : 'top',
38388     labelWidth : 3,
38389     dayAllowBlank : false,
38390     monthAllowBlank : false,
38391     yearAllowBlank : false,
38392     dayPlaceholder : '',
38393     monthPlaceholder : '',
38394     yearPlaceholder : '',
38395     dayFormat : 'd',
38396     monthFormat : 'm',
38397     yearFormat : 'Y',
38398     isFormField : true,
38399     labellg : 0,
38400     labelmd : 0,
38401     labelsm : 0,
38402     labelxs : 0,
38403     
38404     getAutoCreate : function()
38405     {
38406         var cfg = {
38407             tag : 'div',
38408             cls : 'row roo-date-split-field-group',
38409             cn : [
38410                 {
38411                     tag : 'input',
38412                     type : 'hidden',
38413                     cls : 'form-hidden-field roo-date-split-field-group-value',
38414                     name : this.name
38415                 }
38416             ]
38417         };
38418         
38419         var labelCls = 'col-md-12';
38420         var contentCls = 'col-md-4';
38421         
38422         if(this.fieldLabel){
38423             
38424             var label = {
38425                 tag : 'div',
38426                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38427                 cn : [
38428                     {
38429                         tag : 'label',
38430                         html : this.fieldLabel
38431                     }
38432                 ]
38433             };
38434             
38435             if(this.labelAlign == 'left'){
38436             
38437                 if(this.labelWidth > 12){
38438                     label.style = "width: " + this.labelWidth + 'px';
38439                 }
38440
38441                 if(this.labelWidth < 13 && this.labelmd == 0){
38442                     this.labelmd = this.labelWidth;
38443                 }
38444
38445                 if(this.labellg > 0){
38446                     labelCls = ' col-lg-' + this.labellg;
38447                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38448                 }
38449
38450                 if(this.labelmd > 0){
38451                     labelCls = ' col-md-' + this.labelmd;
38452                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38453                 }
38454
38455                 if(this.labelsm > 0){
38456                     labelCls = ' col-sm-' + this.labelsm;
38457                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38458                 }
38459
38460                 if(this.labelxs > 0){
38461                     labelCls = ' col-xs-' + this.labelxs;
38462                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38463                 }
38464             }
38465             
38466             label.cls += ' ' + labelCls;
38467             
38468             cfg.cn.push(label);
38469         }
38470         
38471         Roo.each(['day', 'month', 'year'], function(t){
38472             cfg.cn.push({
38473                 tag : 'div',
38474                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38475             });
38476         }, this);
38477         
38478         return cfg;
38479     },
38480     
38481     inputEl: function ()
38482     {
38483         return this.el.select('.roo-date-split-field-group-value', true).first();
38484     },
38485     
38486     onRender : function(ct, position) 
38487     {
38488         var _this = this;
38489         
38490         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38491         
38492         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38493         
38494         this.dayField = new Roo.bootstrap.form.ComboBox({
38495             allowBlank : this.dayAllowBlank,
38496             alwaysQuery : true,
38497             displayField : 'value',
38498             editable : false,
38499             fieldLabel : '',
38500             forceSelection : true,
38501             mode : 'local',
38502             placeholder : this.dayPlaceholder,
38503             selectOnFocus : true,
38504             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38505             triggerAction : 'all',
38506             typeAhead : true,
38507             valueField : 'value',
38508             store : new Roo.data.SimpleStore({
38509                 data : (function() {    
38510                     var days = [];
38511                     _this.fireEvent('days', _this, days);
38512                     return days;
38513                 })(),
38514                 fields : [ 'value' ]
38515             }),
38516             listeners : {
38517                 select : function (_self, record, index)
38518                 {
38519                     _this.setValue(_this.getValue());
38520                 }
38521             }
38522         });
38523
38524         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38525         
38526         this.monthField = new Roo.bootstrap.form.MonthField({
38527             after : '<i class=\"fa fa-calendar\"></i>',
38528             allowBlank : this.monthAllowBlank,
38529             placeholder : this.monthPlaceholder,
38530             readOnly : true,
38531             listeners : {
38532                 render : function (_self)
38533                 {
38534                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
38535                         e.preventDefault();
38536                         _self.focus();
38537                     });
38538                 },
38539                 select : function (_self, oldvalue, newvalue)
38540                 {
38541                     _this.setValue(_this.getValue());
38542                 }
38543             }
38544         });
38545         
38546         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38547         
38548         this.yearField = new Roo.bootstrap.form.ComboBox({
38549             allowBlank : this.yearAllowBlank,
38550             alwaysQuery : true,
38551             displayField : 'value',
38552             editable : false,
38553             fieldLabel : '',
38554             forceSelection : true,
38555             mode : 'local',
38556             placeholder : this.yearPlaceholder,
38557             selectOnFocus : true,
38558             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38559             triggerAction : 'all',
38560             typeAhead : true,
38561             valueField : 'value',
38562             store : new Roo.data.SimpleStore({
38563                 data : (function() {
38564                     var years = [];
38565                     _this.fireEvent('years', _this, years);
38566                     return years;
38567                 })(),
38568                 fields : [ 'value' ]
38569             }),
38570             listeners : {
38571                 select : function (_self, record, index)
38572                 {
38573                     _this.setValue(_this.getValue());
38574                 }
38575             }
38576         });
38577
38578         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38579     },
38580     
38581     setValue : function(v, format)
38582     {
38583         this.inputEl.dom.value = v;
38584         
38585         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38586         
38587         var d = Date.parseDate(v, f);
38588         
38589         if(!d){
38590             this.validate();
38591             return;
38592         }
38593         
38594         this.setDay(d.format(this.dayFormat));
38595         this.setMonth(d.format(this.monthFormat));
38596         this.setYear(d.format(this.yearFormat));
38597         
38598         this.validate();
38599         
38600         return;
38601     },
38602     
38603     setDay : function(v)
38604     {
38605         this.dayField.setValue(v);
38606         this.inputEl.dom.value = this.getValue();
38607         this.validate();
38608         return;
38609     },
38610     
38611     setMonth : function(v)
38612     {
38613         this.monthField.setValue(v, true);
38614         this.inputEl.dom.value = this.getValue();
38615         this.validate();
38616         return;
38617     },
38618     
38619     setYear : function(v)
38620     {
38621         this.yearField.setValue(v);
38622         this.inputEl.dom.value = this.getValue();
38623         this.validate();
38624         return;
38625     },
38626     
38627     getDay : function()
38628     {
38629         return this.dayField.getValue();
38630     },
38631     
38632     getMonth : function()
38633     {
38634         return this.monthField.getValue();
38635     },
38636     
38637     getYear : function()
38638     {
38639         return this.yearField.getValue();
38640     },
38641     
38642     getValue : function()
38643     {
38644         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38645         
38646         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38647         
38648         return date;
38649     },
38650     
38651     reset : function()
38652     {
38653         this.setDay('');
38654         this.setMonth('');
38655         this.setYear('');
38656         this.inputEl.dom.value = '';
38657         this.validate();
38658         return;
38659     },
38660     
38661     validate : function()
38662     {
38663         var d = this.dayField.validate();
38664         var m = this.monthField.validate();
38665         var y = this.yearField.validate();
38666         
38667         var valid = true;
38668         
38669         if(
38670                 (!this.dayAllowBlank && !d) ||
38671                 (!this.monthAllowBlank && !m) ||
38672                 (!this.yearAllowBlank && !y)
38673         ){
38674             valid = false;
38675         }
38676         
38677         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38678             return valid;
38679         }
38680         
38681         if(valid){
38682             this.markValid();
38683             return valid;
38684         }
38685         
38686         this.markInvalid();
38687         
38688         return valid;
38689     },
38690     
38691     markValid : function()
38692     {
38693         
38694         var label = this.el.select('label', true).first();
38695         var icon = this.el.select('i.fa-star', true).first();
38696
38697         if(label && icon){
38698             icon.remove();
38699         }
38700         
38701         this.fireEvent('valid', this);
38702     },
38703     
38704      /**
38705      * Mark this field as invalid
38706      * @param {String} msg The validation message
38707      */
38708     markInvalid : function(msg)
38709     {
38710         
38711         var label = this.el.select('label', true).first();
38712         var icon = this.el.select('i.fa-star', true).first();
38713
38714         if(label && !icon){
38715             this.el.select('.roo-date-split-field-label', true).createChild({
38716                 tag : 'i',
38717                 cls : 'text-danger fa fa-lg fa-star',
38718                 tooltip : 'This field is required',
38719                 style : 'margin-right:5px;'
38720             }, label, true);
38721         }
38722         
38723         this.fireEvent('invalid', this, msg);
38724     },
38725     
38726     clearInvalid : function()
38727     {
38728         var label = this.el.select('label', true).first();
38729         var icon = this.el.select('i.fa-star', true).first();
38730
38731         if(label && icon){
38732             icon.remove();
38733         }
38734         
38735         this.fireEvent('valid', this);
38736     },
38737     
38738     getName: function()
38739     {
38740         return this.name;
38741     }
38742     
38743 });
38744
38745  
38746
38747 /**
38748  * @class Roo.bootstrap.LayoutMasonry
38749  * @extends Roo.bootstrap.Component
38750  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38751  * Bootstrap Layout Masonry class
38752  *
38753  * This is based on 
38754  * http://masonry.desandro.com
38755  *
38756  * The idea is to render all the bricks based on vertical width...
38757  *
38758  * The original code extends 'outlayer' - we might need to use that....
38759
38760  * @constructor
38761  * Create a new Element
38762  * @param {Object} config The config object
38763  */
38764
38765 Roo.bootstrap.LayoutMasonry = function(config){
38766     
38767     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38768     
38769     this.bricks = [];
38770     
38771     Roo.bootstrap.LayoutMasonry.register(this);
38772     
38773     this.addEvents({
38774         // raw events
38775         /**
38776          * @event layout
38777          * Fire after layout the items
38778          * @param {Roo.bootstrap.LayoutMasonry} this
38779          * @param {Roo.EventObject} e
38780          */
38781         "layout" : true
38782     });
38783     
38784 };
38785
38786 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
38787     
38788     /**
38789      * @cfg {Boolean} isLayoutInstant = no animation?
38790      */   
38791     isLayoutInstant : false, // needed?
38792    
38793     /**
38794      * @cfg {Number} boxWidth  width of the columns
38795      */   
38796     boxWidth : 450,
38797     
38798       /**
38799      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
38800      */   
38801     boxHeight : 0,
38802     
38803     /**
38804      * @cfg {Number} padWidth padding below box..
38805      */   
38806     padWidth : 10, 
38807     
38808     /**
38809      * @cfg {Number} gutter gutter width..
38810      */   
38811     gutter : 10,
38812     
38813      /**
38814      * @cfg {Number} maxCols maximum number of columns
38815      */   
38816     
38817     maxCols: 0,
38818     
38819     /**
38820      * @cfg {Boolean} isAutoInitial defalut true
38821      */   
38822     isAutoInitial : true, 
38823     
38824     containerWidth: 0,
38825     
38826     /**
38827      * @cfg {Boolean} isHorizontal defalut false
38828      */   
38829     isHorizontal : false, 
38830
38831     currentSize : null,
38832     
38833     tag: 'div',
38834     
38835     cls: '',
38836     
38837     bricks: null, //CompositeElement
38838     
38839     cols : 1,
38840     
38841     _isLayoutInited : false,
38842     
38843 //    isAlternative : false, // only use for vertical layout...
38844     
38845     /**
38846      * @cfg {Number} alternativePadWidth padding below box..
38847      */   
38848     alternativePadWidth : 50,
38849     
38850     selectedBrick : [],
38851     
38852     getAutoCreate : function(){
38853         
38854         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38855         
38856         var cfg = {
38857             tag: this.tag,
38858             cls: 'blog-masonary-wrapper ' + this.cls,
38859             cn : {
38860                 cls : 'mas-boxes masonary'
38861             }
38862         };
38863         
38864         return cfg;
38865     },
38866     
38867     getChildContainer: function( )
38868     {
38869         if (this.boxesEl) {
38870             return this.boxesEl;
38871         }
38872         
38873         this.boxesEl = this.el.select('.mas-boxes').first();
38874         
38875         return this.boxesEl;
38876     },
38877     
38878     
38879     initEvents : function()
38880     {
38881         var _this = this;
38882         
38883         if(this.isAutoInitial){
38884             Roo.log('hook children rendered');
38885             this.on('childrenrendered', function() {
38886                 Roo.log('children rendered');
38887                 _this.initial();
38888             } ,this);
38889         }
38890     },
38891     
38892     initial : function()
38893     {
38894         this.selectedBrick = [];
38895         
38896         this.currentSize = this.el.getBox(true);
38897         
38898         Roo.EventManager.onWindowResize(this.resize, this); 
38899
38900         if(!this.isAutoInitial){
38901             this.layout();
38902             return;
38903         }
38904         
38905         this.layout();
38906         
38907         return;
38908         //this.layout.defer(500,this);
38909         
38910     },
38911     
38912     resize : function()
38913     {
38914         var cs = this.el.getBox(true);
38915         
38916         if (
38917                 this.currentSize.width == cs.width && 
38918                 this.currentSize.x == cs.x && 
38919                 this.currentSize.height == cs.height && 
38920                 this.currentSize.y == cs.y 
38921         ) {
38922             Roo.log("no change in with or X or Y");
38923             return;
38924         }
38925         
38926         this.currentSize = cs;
38927         
38928         this.layout();
38929         
38930     },
38931     
38932     layout : function()
38933     {   
38934         this._resetLayout();
38935         
38936         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38937         
38938         this.layoutItems( isInstant );
38939       
38940         this._isLayoutInited = true;
38941         
38942         this.fireEvent('layout', this);
38943         
38944     },
38945     
38946     _resetLayout : function()
38947     {
38948         if(this.isHorizontal){
38949             this.horizontalMeasureColumns();
38950             return;
38951         }
38952         
38953         this.verticalMeasureColumns();
38954         
38955     },
38956     
38957     verticalMeasureColumns : function()
38958     {
38959         this.getContainerWidth();
38960         
38961 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38962 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
38963 //            return;
38964 //        }
38965         
38966         var boxWidth = this.boxWidth + this.padWidth;
38967         
38968         if(this.containerWidth < this.boxWidth){
38969             boxWidth = this.containerWidth
38970         }
38971         
38972         var containerWidth = this.containerWidth;
38973         
38974         var cols = Math.floor(containerWidth / boxWidth);
38975         
38976         this.cols = Math.max( cols, 1 );
38977         
38978         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38979         
38980         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38981         
38982         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38983         
38984         this.colWidth = boxWidth + avail - this.padWidth;
38985         
38986         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38987         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
38988     },
38989     
38990     horizontalMeasureColumns : function()
38991     {
38992         this.getContainerWidth();
38993         
38994         var boxWidth = this.boxWidth;
38995         
38996         if(this.containerWidth < boxWidth){
38997             boxWidth = this.containerWidth;
38998         }
38999         
39000         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
39001         
39002         this.el.setHeight(boxWidth);
39003         
39004     },
39005     
39006     getContainerWidth : function()
39007     {
39008         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39009     },
39010     
39011     layoutItems : function( isInstant )
39012     {
39013         Roo.log(this.bricks);
39014         
39015         var items = Roo.apply([], this.bricks);
39016         
39017         if(this.isHorizontal){
39018             this._horizontalLayoutItems( items , isInstant );
39019             return;
39020         }
39021         
39022 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39023 //            this._verticalAlternativeLayoutItems( items , isInstant );
39024 //            return;
39025 //        }
39026         
39027         this._verticalLayoutItems( items , isInstant );
39028         
39029     },
39030     
39031     _verticalLayoutItems : function ( items , isInstant)
39032     {
39033         if ( !items || !items.length ) {
39034             return;
39035         }
39036         
39037         var standard = [
39038             ['xs', 'xs', 'xs', 'tall'],
39039             ['xs', 'xs', 'tall'],
39040             ['xs', 'xs', 'sm'],
39041             ['xs', 'xs', 'xs'],
39042             ['xs', 'tall'],
39043             ['xs', 'sm'],
39044             ['xs', 'xs'],
39045             ['xs'],
39046             
39047             ['sm', 'xs', 'xs'],
39048             ['sm', 'xs'],
39049             ['sm'],
39050             
39051             ['tall', 'xs', 'xs', 'xs'],
39052             ['tall', 'xs', 'xs'],
39053             ['tall', 'xs'],
39054             ['tall']
39055             
39056         ];
39057         
39058         var queue = [];
39059         
39060         var boxes = [];
39061         
39062         var box = [];
39063         
39064         Roo.each(items, function(item, k){
39065             
39066             switch (item.size) {
39067                 // these layouts take up a full box,
39068                 case 'md' :
39069                 case 'md-left' :
39070                 case 'md-right' :
39071                 case 'wide' :
39072                     
39073                     if(box.length){
39074                         boxes.push(box);
39075                         box = [];
39076                     }
39077                     
39078                     boxes.push([item]);
39079                     
39080                     break;
39081                     
39082                 case 'xs' :
39083                 case 'sm' :
39084                 case 'tall' :
39085                     
39086                     box.push(item);
39087                     
39088                     break;
39089                 default :
39090                     break;
39091                     
39092             }
39093             
39094         }, this);
39095         
39096         if(box.length){
39097             boxes.push(box);
39098             box = [];
39099         }
39100         
39101         var filterPattern = function(box, length)
39102         {
39103             if(!box.length){
39104                 return;
39105             }
39106             
39107             var match = false;
39108             
39109             var pattern = box.slice(0, length);
39110             
39111             var format = [];
39112             
39113             Roo.each(pattern, function(i){
39114                 format.push(i.size);
39115             }, this);
39116             
39117             Roo.each(standard, function(s){
39118                 
39119                 if(String(s) != String(format)){
39120                     return;
39121                 }
39122                 
39123                 match = true;
39124                 return false;
39125                 
39126             }, this);
39127             
39128             if(!match && length == 1){
39129                 return;
39130             }
39131             
39132             if(!match){
39133                 filterPattern(box, length - 1);
39134                 return;
39135             }
39136                 
39137             queue.push(pattern);
39138
39139             box = box.slice(length, box.length);
39140
39141             filterPattern(box, 4);
39142
39143             return;
39144             
39145         }
39146         
39147         Roo.each(boxes, function(box, k){
39148             
39149             if(!box.length){
39150                 return;
39151             }
39152             
39153             if(box.length == 1){
39154                 queue.push(box);
39155                 return;
39156             }
39157             
39158             filterPattern(box, 4);
39159             
39160         }, this);
39161         
39162         this._processVerticalLayoutQueue( queue, isInstant );
39163         
39164     },
39165     
39166 //    _verticalAlternativeLayoutItems : function( items , isInstant )
39167 //    {
39168 //        if ( !items || !items.length ) {
39169 //            return;
39170 //        }
39171 //
39172 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
39173 //        
39174 //    },
39175     
39176     _horizontalLayoutItems : function ( items , isInstant)
39177     {
39178         if ( !items || !items.length || items.length < 3) {
39179             return;
39180         }
39181         
39182         items.reverse();
39183         
39184         var eItems = items.slice(0, 3);
39185         
39186         items = items.slice(3, items.length);
39187         
39188         var standard = [
39189             ['xs', 'xs', 'xs', 'wide'],
39190             ['xs', 'xs', 'wide'],
39191             ['xs', 'xs', 'sm'],
39192             ['xs', 'xs', 'xs'],
39193             ['xs', 'wide'],
39194             ['xs', 'sm'],
39195             ['xs', 'xs'],
39196             ['xs'],
39197             
39198             ['sm', 'xs', 'xs'],
39199             ['sm', 'xs'],
39200             ['sm'],
39201             
39202             ['wide', 'xs', 'xs', 'xs'],
39203             ['wide', 'xs', 'xs'],
39204             ['wide', 'xs'],
39205             ['wide'],
39206             
39207             ['wide-thin']
39208         ];
39209         
39210         var queue = [];
39211         
39212         var boxes = [];
39213         
39214         var box = [];
39215         
39216         Roo.each(items, function(item, k){
39217             
39218             switch (item.size) {
39219                 case 'md' :
39220                 case 'md-left' :
39221                 case 'md-right' :
39222                 case 'tall' :
39223                     
39224                     if(box.length){
39225                         boxes.push(box);
39226                         box = [];
39227                     }
39228                     
39229                     boxes.push([item]);
39230                     
39231                     break;
39232                     
39233                 case 'xs' :
39234                 case 'sm' :
39235                 case 'wide' :
39236                 case 'wide-thin' :
39237                     
39238                     box.push(item);
39239                     
39240                     break;
39241                 default :
39242                     break;
39243                     
39244             }
39245             
39246         }, this);
39247         
39248         if(box.length){
39249             boxes.push(box);
39250             box = [];
39251         }
39252         
39253         var filterPattern = function(box, length)
39254         {
39255             if(!box.length){
39256                 return;
39257             }
39258             
39259             var match = false;
39260             
39261             var pattern = box.slice(0, length);
39262             
39263             var format = [];
39264             
39265             Roo.each(pattern, function(i){
39266                 format.push(i.size);
39267             }, this);
39268             
39269             Roo.each(standard, function(s){
39270                 
39271                 if(String(s) != String(format)){
39272                     return;
39273                 }
39274                 
39275                 match = true;
39276                 return false;
39277                 
39278             }, this);
39279             
39280             if(!match && length == 1){
39281                 return;
39282             }
39283             
39284             if(!match){
39285                 filterPattern(box, length - 1);
39286                 return;
39287             }
39288                 
39289             queue.push(pattern);
39290
39291             box = box.slice(length, box.length);
39292
39293             filterPattern(box, 4);
39294
39295             return;
39296             
39297         }
39298         
39299         Roo.each(boxes, function(box, k){
39300             
39301             if(!box.length){
39302                 return;
39303             }
39304             
39305             if(box.length == 1){
39306                 queue.push(box);
39307                 return;
39308             }
39309             
39310             filterPattern(box, 4);
39311             
39312         }, this);
39313         
39314         
39315         var prune = [];
39316         
39317         var pos = this.el.getBox(true);
39318         
39319         var minX = pos.x;
39320         
39321         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39322         
39323         var hit_end = false;
39324         
39325         Roo.each(queue, function(box){
39326             
39327             if(hit_end){
39328                 
39329                 Roo.each(box, function(b){
39330                 
39331                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39332                     b.el.hide();
39333
39334                 }, this);
39335
39336                 return;
39337             }
39338             
39339             var mx = 0;
39340             
39341             Roo.each(box, function(b){
39342                 
39343                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39344                 b.el.show();
39345
39346                 mx = Math.max(mx, b.x);
39347                 
39348             }, this);
39349             
39350             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39351             
39352             if(maxX < minX){
39353                 
39354                 Roo.each(box, function(b){
39355                 
39356                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39357                     b.el.hide();
39358                     
39359                 }, this);
39360                 
39361                 hit_end = true;
39362                 
39363                 return;
39364             }
39365             
39366             prune.push(box);
39367             
39368         }, this);
39369         
39370         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39371     },
39372     
39373     /** Sets position of item in DOM
39374     * @param {Element} item
39375     * @param {Number} x - horizontal position
39376     * @param {Number} y - vertical position
39377     * @param {Boolean} isInstant - disables transitions
39378     */
39379     _processVerticalLayoutQueue : function( queue, isInstant )
39380     {
39381         var pos = this.el.getBox(true);
39382         var x = pos.x;
39383         var y = pos.y;
39384         var maxY = [];
39385         
39386         for (var i = 0; i < this.cols; i++){
39387             maxY[i] = pos.y;
39388         }
39389         
39390         Roo.each(queue, function(box, k){
39391             
39392             var col = k % this.cols;
39393             
39394             Roo.each(box, function(b,kk){
39395                 
39396                 b.el.position('absolute');
39397                 
39398                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39399                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39400                 
39401                 if(b.size == 'md-left' || b.size == 'md-right'){
39402                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39403                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39404                 }
39405                 
39406                 b.el.setWidth(width);
39407                 b.el.setHeight(height);
39408                 // iframe?
39409                 b.el.select('iframe',true).setSize(width,height);
39410                 
39411             }, this);
39412             
39413             for (var i = 0; i < this.cols; i++){
39414                 
39415                 if(maxY[i] < maxY[col]){
39416                     col = i;
39417                     continue;
39418                 }
39419                 
39420                 col = Math.min(col, i);
39421                 
39422             }
39423             
39424             x = pos.x + col * (this.colWidth + this.padWidth);
39425             
39426             y = maxY[col];
39427             
39428             var positions = [];
39429             
39430             switch (box.length){
39431                 case 1 :
39432                     positions = this.getVerticalOneBoxColPositions(x, y, box);
39433                     break;
39434                 case 2 :
39435                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
39436                     break;
39437                 case 3 :
39438                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
39439                     break;
39440                 case 4 :
39441                     positions = this.getVerticalFourBoxColPositions(x, y, box);
39442                     break;
39443                 default :
39444                     break;
39445             }
39446             
39447             Roo.each(box, function(b,kk){
39448                 
39449                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39450                 
39451                 var sz = b.el.getSize();
39452                 
39453                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39454                 
39455             }, this);
39456             
39457         }, this);
39458         
39459         var mY = 0;
39460         
39461         for (var i = 0; i < this.cols; i++){
39462             mY = Math.max(mY, maxY[i]);
39463         }
39464         
39465         this.el.setHeight(mY - pos.y);
39466         
39467     },
39468     
39469 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39470 //    {
39471 //        var pos = this.el.getBox(true);
39472 //        var x = pos.x;
39473 //        var y = pos.y;
39474 //        var maxX = pos.right;
39475 //        
39476 //        var maxHeight = 0;
39477 //        
39478 //        Roo.each(items, function(item, k){
39479 //            
39480 //            var c = k % 2;
39481 //            
39482 //            item.el.position('absolute');
39483 //                
39484 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39485 //
39486 //            item.el.setWidth(width);
39487 //
39488 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39489 //
39490 //            item.el.setHeight(height);
39491 //            
39492 //            if(c == 0){
39493 //                item.el.setXY([x, y], isInstant ? false : true);
39494 //            } else {
39495 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
39496 //            }
39497 //            
39498 //            y = y + height + this.alternativePadWidth;
39499 //            
39500 //            maxHeight = maxHeight + height + this.alternativePadWidth;
39501 //            
39502 //        }, this);
39503 //        
39504 //        this.el.setHeight(maxHeight);
39505 //        
39506 //    },
39507     
39508     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39509     {
39510         var pos = this.el.getBox(true);
39511         
39512         var minX = pos.x;
39513         var minY = pos.y;
39514         
39515         var maxX = pos.right;
39516         
39517         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39518         
39519         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39520         
39521         Roo.each(queue, function(box, k){
39522             
39523             Roo.each(box, function(b, kk){
39524                 
39525                 b.el.position('absolute');
39526                 
39527                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39528                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39529                 
39530                 if(b.size == 'md-left' || b.size == 'md-right'){
39531                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39532                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39533                 }
39534                 
39535                 b.el.setWidth(width);
39536                 b.el.setHeight(height);
39537                 
39538             }, this);
39539             
39540             if(!box.length){
39541                 return;
39542             }
39543             
39544             var positions = [];
39545             
39546             switch (box.length){
39547                 case 1 :
39548                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39549                     break;
39550                 case 2 :
39551                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39552                     break;
39553                 case 3 :
39554                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39555                     break;
39556                 case 4 :
39557                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39558                     break;
39559                 default :
39560                     break;
39561             }
39562             
39563             Roo.each(box, function(b,kk){
39564                 
39565                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39566                 
39567                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39568                 
39569             }, this);
39570             
39571         }, this);
39572         
39573     },
39574     
39575     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39576     {
39577         Roo.each(eItems, function(b,k){
39578             
39579             b.size = (k == 0) ? 'sm' : 'xs';
39580             b.x = (k == 0) ? 2 : 1;
39581             b.y = (k == 0) ? 2 : 1;
39582             
39583             b.el.position('absolute');
39584             
39585             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39586                 
39587             b.el.setWidth(width);
39588             
39589             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39590             
39591             b.el.setHeight(height);
39592             
39593         }, this);
39594
39595         var positions = [];
39596         
39597         positions.push({
39598             x : maxX - this.unitWidth * 2 - this.gutter,
39599             y : minY
39600         });
39601         
39602         positions.push({
39603             x : maxX - this.unitWidth,
39604             y : minY + (this.unitWidth + this.gutter) * 2
39605         });
39606         
39607         positions.push({
39608             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39609             y : minY
39610         });
39611         
39612         Roo.each(eItems, function(b,k){
39613             
39614             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39615
39616         }, this);
39617         
39618     },
39619     
39620     getVerticalOneBoxColPositions : function(x, y, box)
39621     {
39622         var pos = [];
39623         
39624         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39625         
39626         if(box[0].size == 'md-left'){
39627             rand = 0;
39628         }
39629         
39630         if(box[0].size == 'md-right'){
39631             rand = 1;
39632         }
39633         
39634         pos.push({
39635             x : x + (this.unitWidth + this.gutter) * rand,
39636             y : y
39637         });
39638         
39639         return pos;
39640     },
39641     
39642     getVerticalTwoBoxColPositions : function(x, y, box)
39643     {
39644         var pos = [];
39645         
39646         if(box[0].size == 'xs'){
39647             
39648             pos.push({
39649                 x : x,
39650                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39651             });
39652
39653             pos.push({
39654                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39655                 y : y
39656             });
39657             
39658             return pos;
39659             
39660         }
39661         
39662         pos.push({
39663             x : x,
39664             y : y
39665         });
39666
39667         pos.push({
39668             x : x + (this.unitWidth + this.gutter) * 2,
39669             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39670         });
39671         
39672         return pos;
39673         
39674     },
39675     
39676     getVerticalThreeBoxColPositions : function(x, y, box)
39677     {
39678         var pos = [];
39679         
39680         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39681             
39682             pos.push({
39683                 x : x,
39684                 y : y
39685             });
39686
39687             pos.push({
39688                 x : x + (this.unitWidth + this.gutter) * 1,
39689                 y : y
39690             });
39691             
39692             pos.push({
39693                 x : x + (this.unitWidth + this.gutter) * 2,
39694                 y : y
39695             });
39696             
39697             return pos;
39698             
39699         }
39700         
39701         if(box[0].size == 'xs' && box[1].size == 'xs'){
39702             
39703             pos.push({
39704                 x : x,
39705                 y : y
39706             });
39707
39708             pos.push({
39709                 x : x,
39710                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39711             });
39712             
39713             pos.push({
39714                 x : x + (this.unitWidth + this.gutter) * 1,
39715                 y : y
39716             });
39717             
39718             return pos;
39719             
39720         }
39721         
39722         pos.push({
39723             x : x,
39724             y : y
39725         });
39726
39727         pos.push({
39728             x : x + (this.unitWidth + this.gutter) * 2,
39729             y : y
39730         });
39731
39732         pos.push({
39733             x : x + (this.unitWidth + this.gutter) * 2,
39734             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39735         });
39736             
39737         return pos;
39738         
39739     },
39740     
39741     getVerticalFourBoxColPositions : function(x, y, box)
39742     {
39743         var pos = [];
39744         
39745         if(box[0].size == 'xs'){
39746             
39747             pos.push({
39748                 x : x,
39749                 y : y
39750             });
39751
39752             pos.push({
39753                 x : x,
39754                 y : y + (this.unitHeight + this.gutter) * 1
39755             });
39756             
39757             pos.push({
39758                 x : x,
39759                 y : y + (this.unitHeight + this.gutter) * 2
39760             });
39761             
39762             pos.push({
39763                 x : x + (this.unitWidth + this.gutter) * 1,
39764                 y : y
39765             });
39766             
39767             return pos;
39768             
39769         }
39770         
39771         pos.push({
39772             x : x,
39773             y : y
39774         });
39775
39776         pos.push({
39777             x : x + (this.unitWidth + this.gutter) * 2,
39778             y : y
39779         });
39780
39781         pos.push({
39782             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39783             y : y + (this.unitHeight + this.gutter) * 1
39784         });
39785
39786         pos.push({
39787             x : x + (this.unitWidth + this.gutter) * 2,
39788             y : y + (this.unitWidth + this.gutter) * 2
39789         });
39790
39791         return pos;
39792         
39793     },
39794     
39795     getHorizontalOneBoxColPositions : function(maxX, minY, box)
39796     {
39797         var pos = [];
39798         
39799         if(box[0].size == 'md-left'){
39800             pos.push({
39801                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39802                 y : minY
39803             });
39804             
39805             return pos;
39806         }
39807         
39808         if(box[0].size == 'md-right'){
39809             pos.push({
39810                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39811                 y : minY + (this.unitWidth + this.gutter) * 1
39812             });
39813             
39814             return pos;
39815         }
39816         
39817         var rand = Math.floor(Math.random() * (4 - box[0].y));
39818         
39819         pos.push({
39820             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39821             y : minY + (this.unitWidth + this.gutter) * rand
39822         });
39823         
39824         return pos;
39825         
39826     },
39827     
39828     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39829     {
39830         var pos = [];
39831         
39832         if(box[0].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[1].x - this.gutter * (box[1].x - 1),
39841                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39842             });
39843             
39844             return pos;
39845             
39846         }
39847         
39848         pos.push({
39849             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39850             y : minY
39851         });
39852
39853         pos.push({
39854             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39855             y : minY + (this.unitWidth + this.gutter) * 2
39856         });
39857         
39858         return pos;
39859         
39860     },
39861     
39862     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39863     {
39864         var pos = [];
39865         
39866         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39867             
39868             pos.push({
39869                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39870                 y : minY
39871             });
39872
39873             pos.push({
39874                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39875                 y : minY + (this.unitWidth + this.gutter) * 1
39876             });
39877             
39878             pos.push({
39879                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39880                 y : minY + (this.unitWidth + this.gutter) * 2
39881             });
39882             
39883             return pos;
39884             
39885         }
39886         
39887         if(box[0].size == 'xs' && box[1].size == 'xs'){
39888             
39889             pos.push({
39890                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39891                 y : minY
39892             });
39893
39894             pos.push({
39895                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39896                 y : minY
39897             });
39898             
39899             pos.push({
39900                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39901                 y : minY + (this.unitWidth + this.gutter) * 1
39902             });
39903             
39904             return pos;
39905             
39906         }
39907         
39908         pos.push({
39909             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39910             y : minY
39911         });
39912
39913         pos.push({
39914             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39915             y : minY + (this.unitWidth + this.gutter) * 2
39916         });
39917
39918         pos.push({
39919             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39920             y : minY + (this.unitWidth + this.gutter) * 2
39921         });
39922             
39923         return pos;
39924         
39925     },
39926     
39927     getHorizontalFourBoxColPositions : function(maxX, minY, box)
39928     {
39929         var pos = [];
39930         
39931         if(box[0].size == 'xs'){
39932             
39933             pos.push({
39934                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39935                 y : minY
39936             });
39937
39938             pos.push({
39939                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39940                 y : minY
39941             });
39942             
39943             pos.push({
39944                 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),
39945                 y : minY
39946             });
39947             
39948             pos.push({
39949                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39950                 y : minY + (this.unitWidth + this.gutter) * 1
39951             });
39952             
39953             return pos;
39954             
39955         }
39956         
39957         pos.push({
39958             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39959             y : minY
39960         });
39961         
39962         pos.push({
39963             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39964             y : minY + (this.unitWidth + this.gutter) * 2
39965         });
39966         
39967         pos.push({
39968             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39969             y : minY + (this.unitWidth + this.gutter) * 2
39970         });
39971         
39972         pos.push({
39973             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),
39974             y : minY + (this.unitWidth + this.gutter) * 2
39975         });
39976
39977         return pos;
39978         
39979     },
39980     
39981     /**
39982     * remove a Masonry Brick
39983     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39984     */
39985     removeBrick : function(brick_id)
39986     {
39987         if (!brick_id) {
39988             return;
39989         }
39990         
39991         for (var i = 0; i<this.bricks.length; i++) {
39992             if (this.bricks[i].id == brick_id) {
39993                 this.bricks.splice(i,1);
39994                 this.el.dom.removeChild(Roo.get(brick_id).dom);
39995                 this.initial();
39996             }
39997         }
39998     },
39999     
40000     /**
40001     * adds a Masonry Brick
40002     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40003     */
40004     addBrick : function(cfg)
40005     {
40006         var cn = new Roo.bootstrap.MasonryBrick(cfg);
40007         //this.register(cn);
40008         cn.parentId = this.id;
40009         cn.render(this.el);
40010         return cn;
40011     },
40012     
40013     /**
40014     * register a Masonry Brick
40015     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40016     */
40017     
40018     register : function(brick)
40019     {
40020         this.bricks.push(brick);
40021         brick.masonryId = this.id;
40022     },
40023     
40024     /**
40025     * clear all the Masonry Brick
40026     */
40027     clearAll : function()
40028     {
40029         this.bricks = [];
40030         //this.getChildContainer().dom.innerHTML = "";
40031         this.el.dom.innerHTML = '';
40032     },
40033     
40034     getSelected : function()
40035     {
40036         if (!this.selectedBrick) {
40037             return false;
40038         }
40039         
40040         return this.selectedBrick;
40041     }
40042 });
40043
40044 Roo.apply(Roo.bootstrap.LayoutMasonry, {
40045     
40046     groups: {},
40047      /**
40048     * register a Masonry Layout
40049     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
40050     */
40051     
40052     register : function(layout)
40053     {
40054         this.groups[layout.id] = layout;
40055     },
40056     /**
40057     * fetch a  Masonry Layout based on the masonry layout ID
40058     * @param {string} the masonry layout to add
40059     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40060     */
40061     
40062     get: function(layout_id) {
40063         if (typeof(this.groups[layout_id]) == 'undefined') {
40064             return false;
40065         }
40066         return this.groups[layout_id] ;
40067     }
40068     
40069     
40070     
40071 });
40072
40073  
40074
40075  /**
40076  *
40077  * This is based on 
40078  * http://masonry.desandro.com
40079  *
40080  * The idea is to render all the bricks based on vertical width...
40081  *
40082  * The original code extends 'outlayer' - we might need to use that....
40083  * 
40084  */
40085
40086
40087 /**
40088  * @class Roo.bootstrap.LayoutMasonryAuto
40089  * @extends Roo.bootstrap.Component
40090  * Bootstrap Layout Masonry class
40091  * 
40092  * @constructor
40093  * Create a new Element
40094  * @param {Object} config The config object
40095  */
40096
40097 Roo.bootstrap.LayoutMasonryAuto = function(config){
40098     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40099 };
40100
40101 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
40102     
40103       /**
40104      * @cfg {Boolean} isFitWidth  - resize the width..
40105      */   
40106     isFitWidth : false,  // options..
40107     /**
40108      * @cfg {Boolean} isOriginLeft = left align?
40109      */   
40110     isOriginLeft : true,
40111     /**
40112      * @cfg {Boolean} isOriginTop = top align?
40113      */   
40114     isOriginTop : false,
40115     /**
40116      * @cfg {Boolean} isLayoutInstant = no animation?
40117      */   
40118     isLayoutInstant : false, // needed?
40119     /**
40120      * @cfg {Boolean} isResizingContainer = not sure if this is used..
40121      */   
40122     isResizingContainer : true,
40123     /**
40124      * @cfg {Number} columnWidth  width of the columns 
40125      */   
40126     
40127     columnWidth : 0,
40128     
40129     /**
40130      * @cfg {Number} maxCols maximum number of columns
40131      */   
40132     
40133     maxCols: 0,
40134     /**
40135      * @cfg {Number} padHeight padding below box..
40136      */   
40137     
40138     padHeight : 10, 
40139     
40140     /**
40141      * @cfg {Boolean} isAutoInitial defalut true
40142      */   
40143     
40144     isAutoInitial : true, 
40145     
40146     // private?
40147     gutter : 0,
40148     
40149     containerWidth: 0,
40150     initialColumnWidth : 0,
40151     currentSize : null,
40152     
40153     colYs : null, // array.
40154     maxY : 0,
40155     padWidth: 10,
40156     
40157     
40158     tag: 'div',
40159     cls: '',
40160     bricks: null, //CompositeElement
40161     cols : 0, // array?
40162     // element : null, // wrapped now this.el
40163     _isLayoutInited : null, 
40164     
40165     
40166     getAutoCreate : function(){
40167         
40168         var cfg = {
40169             tag: this.tag,
40170             cls: 'blog-masonary-wrapper ' + this.cls,
40171             cn : {
40172                 cls : 'mas-boxes masonary'
40173             }
40174         };
40175         
40176         return cfg;
40177     },
40178     
40179     getChildContainer: function( )
40180     {
40181         if (this.boxesEl) {
40182             return this.boxesEl;
40183         }
40184         
40185         this.boxesEl = this.el.select('.mas-boxes').first();
40186         
40187         return this.boxesEl;
40188     },
40189     
40190     
40191     initEvents : function()
40192     {
40193         var _this = this;
40194         
40195         if(this.isAutoInitial){
40196             Roo.log('hook children rendered');
40197             this.on('childrenrendered', function() {
40198                 Roo.log('children rendered');
40199                 _this.initial();
40200             } ,this);
40201         }
40202         
40203     },
40204     
40205     initial : function()
40206     {
40207         this.reloadItems();
40208
40209         this.currentSize = this.el.getBox(true);
40210
40211         /// was window resize... - let's see if this works..
40212         Roo.EventManager.onWindowResize(this.resize, this); 
40213
40214         if(!this.isAutoInitial){
40215             this.layout();
40216             return;
40217         }
40218         
40219         this.layout.defer(500,this);
40220     },
40221     
40222     reloadItems: function()
40223     {
40224         this.bricks = this.el.select('.masonry-brick', true);
40225         
40226         this.bricks.each(function(b) {
40227             //Roo.log(b.getSize());
40228             if (!b.attr('originalwidth')) {
40229                 b.attr('originalwidth',  b.getSize().width);
40230             }
40231             
40232         });
40233         
40234         Roo.log(this.bricks.elements.length);
40235     },
40236     
40237     resize : function()
40238     {
40239         Roo.log('resize');
40240         var cs = this.el.getBox(true);
40241         
40242         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40243             Roo.log("no change in with or X");
40244             return;
40245         }
40246         this.currentSize = cs;
40247         this.layout();
40248     },
40249     
40250     layout : function()
40251     {
40252          Roo.log('layout');
40253         this._resetLayout();
40254         //this._manageStamps();
40255       
40256         // don't animate first layout
40257         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40258         this.layoutItems( isInstant );
40259       
40260         // flag for initalized
40261         this._isLayoutInited = true;
40262     },
40263     
40264     layoutItems : function( isInstant )
40265     {
40266         //var items = this._getItemsForLayout( this.items );
40267         // original code supports filtering layout items.. we just ignore it..
40268         
40269         this._layoutItems( this.bricks , isInstant );
40270       
40271         this._postLayout();
40272     },
40273     _layoutItems : function ( items , isInstant)
40274     {
40275        //this.fireEvent( 'layout', this, items );
40276     
40277
40278         if ( !items || !items.elements.length ) {
40279           // no items, emit event with empty array
40280             return;
40281         }
40282
40283         var queue = [];
40284         items.each(function(item) {
40285             Roo.log("layout item");
40286             Roo.log(item);
40287             // get x/y object from method
40288             var position = this._getItemLayoutPosition( item );
40289             // enqueue
40290             position.item = item;
40291             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40292             queue.push( position );
40293         }, this);
40294       
40295         this._processLayoutQueue( queue );
40296     },
40297     /** Sets position of item in DOM
40298     * @param {Element} item
40299     * @param {Number} x - horizontal position
40300     * @param {Number} y - vertical position
40301     * @param {Boolean} isInstant - disables transitions
40302     */
40303     _processLayoutQueue : function( queue )
40304     {
40305         for ( var i=0, len = queue.length; i < len; i++ ) {
40306             var obj = queue[i];
40307             obj.item.position('absolute');
40308             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40309         }
40310     },
40311       
40312     
40313     /**
40314     * Any logic you want to do after each layout,
40315     * i.e. size the container
40316     */
40317     _postLayout : function()
40318     {
40319         this.resizeContainer();
40320     },
40321     
40322     resizeContainer : function()
40323     {
40324         if ( !this.isResizingContainer ) {
40325             return;
40326         }
40327         var size = this._getContainerSize();
40328         if ( size ) {
40329             this.el.setSize(size.width,size.height);
40330             this.boxesEl.setSize(size.width,size.height);
40331         }
40332     },
40333     
40334     
40335     
40336     _resetLayout : function()
40337     {
40338         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40339         this.colWidth = this.el.getWidth();
40340         //this.gutter = this.el.getWidth(); 
40341         
40342         this.measureColumns();
40343
40344         // reset column Y
40345         var i = this.cols;
40346         this.colYs = [];
40347         while (i--) {
40348             this.colYs.push( 0 );
40349         }
40350     
40351         this.maxY = 0;
40352     },
40353
40354     measureColumns : function()
40355     {
40356         this.getContainerWidth();
40357       // if columnWidth is 0, default to outerWidth of first item
40358         if ( !this.columnWidth ) {
40359             var firstItem = this.bricks.first();
40360             Roo.log(firstItem);
40361             this.columnWidth  = this.containerWidth;
40362             if (firstItem && firstItem.attr('originalwidth') ) {
40363                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40364             }
40365             // columnWidth fall back to item of first element
40366             Roo.log("set column width?");
40367                         this.initialColumnWidth = this.columnWidth  ;
40368
40369             // if first elem has no width, default to size of container
40370             
40371         }
40372         
40373         
40374         if (this.initialColumnWidth) {
40375             this.columnWidth = this.initialColumnWidth;
40376         }
40377         
40378         
40379             
40380         // column width is fixed at the top - however if container width get's smaller we should
40381         // reduce it...
40382         
40383         // this bit calcs how man columns..
40384             
40385         var columnWidth = this.columnWidth += this.gutter;
40386       
40387         // calculate columns
40388         var containerWidth = this.containerWidth + this.gutter;
40389         
40390         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40391         // fix rounding errors, typically with gutters
40392         var excess = columnWidth - containerWidth % columnWidth;
40393         
40394         
40395         // if overshoot is less than a pixel, round up, otherwise floor it
40396         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40397         cols = Math[ mathMethod ]( cols );
40398         this.cols = Math.max( cols, 1 );
40399         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40400         
40401          // padding positioning..
40402         var totalColWidth = this.cols * this.columnWidth;
40403         var padavail = this.containerWidth - totalColWidth;
40404         // so for 2 columns - we need 3 'pads'
40405         
40406         var padNeeded = (1+this.cols) * this.padWidth;
40407         
40408         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40409         
40410         this.columnWidth += padExtra
40411         //this.padWidth = Math.floor(padavail /  ( this.cols));
40412         
40413         // adjust colum width so that padding is fixed??
40414         
40415         // we have 3 columns ... total = width * 3
40416         // we have X left over... that should be used by 
40417         
40418         //if (this.expandC) {
40419             
40420         //}
40421         
40422         
40423         
40424     },
40425     
40426     getContainerWidth : function()
40427     {
40428        /* // container is parent if fit width
40429         var container = this.isFitWidth ? this.element.parentNode : this.element;
40430         // check that this.size and size are there
40431         // IE8 triggers resize on body size change, so they might not be
40432         
40433         var size = getSize( container );  //FIXME
40434         this.containerWidth = size && size.innerWidth; //FIXME
40435         */
40436          
40437         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
40438         
40439     },
40440     
40441     _getItemLayoutPosition : function( item )  // what is item?
40442     {
40443         // we resize the item to our columnWidth..
40444       
40445         item.setWidth(this.columnWidth);
40446         item.autoBoxAdjust  = false;
40447         
40448         var sz = item.getSize();
40449  
40450         // how many columns does this brick span
40451         var remainder = this.containerWidth % this.columnWidth;
40452         
40453         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40454         // round if off by 1 pixel, otherwise use ceil
40455         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
40456         colSpan = Math.min( colSpan, this.cols );
40457         
40458         // normally this should be '1' as we dont' currently allow multi width columns..
40459         
40460         var colGroup = this._getColGroup( colSpan );
40461         // get the minimum Y value from the columns
40462         var minimumY = Math.min.apply( Math, colGroup );
40463         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40464         
40465         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
40466          
40467         // position the brick
40468         var position = {
40469             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40470             y: this.currentSize.y + minimumY + this.padHeight
40471         };
40472         
40473         Roo.log(position);
40474         // apply setHeight to necessary columns
40475         var setHeight = minimumY + sz.height + this.padHeight;
40476         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40477         
40478         var setSpan = this.cols + 1 - colGroup.length;
40479         for ( var i = 0; i < setSpan; i++ ) {
40480           this.colYs[ shortColIndex + i ] = setHeight ;
40481         }
40482       
40483         return position;
40484     },
40485     
40486     /**
40487      * @param {Number} colSpan - number of columns the element spans
40488      * @returns {Array} colGroup
40489      */
40490     _getColGroup : function( colSpan )
40491     {
40492         if ( colSpan < 2 ) {
40493           // if brick spans only one column, use all the column Ys
40494           return this.colYs;
40495         }
40496       
40497         var colGroup = [];
40498         // how many different places could this brick fit horizontally
40499         var groupCount = this.cols + 1 - colSpan;
40500         // for each group potential horizontal position
40501         for ( var i = 0; i < groupCount; i++ ) {
40502           // make an array of colY values for that one group
40503           var groupColYs = this.colYs.slice( i, i + colSpan );
40504           // and get the max value of the array
40505           colGroup[i] = Math.max.apply( Math, groupColYs );
40506         }
40507         return colGroup;
40508     },
40509     /*
40510     _manageStamp : function( stamp )
40511     {
40512         var stampSize =  stamp.getSize();
40513         var offset = stamp.getBox();
40514         // get the columns that this stamp affects
40515         var firstX = this.isOriginLeft ? offset.x : offset.right;
40516         var lastX = firstX + stampSize.width;
40517         var firstCol = Math.floor( firstX / this.columnWidth );
40518         firstCol = Math.max( 0, firstCol );
40519         
40520         var lastCol = Math.floor( lastX / this.columnWidth );
40521         // lastCol should not go over if multiple of columnWidth #425
40522         lastCol -= lastX % this.columnWidth ? 0 : 1;
40523         lastCol = Math.min( this.cols - 1, lastCol );
40524         
40525         // set colYs to bottom of the stamp
40526         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40527             stampSize.height;
40528             
40529         for ( var i = firstCol; i <= lastCol; i++ ) {
40530           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40531         }
40532     },
40533     */
40534     
40535     _getContainerSize : function()
40536     {
40537         this.maxY = Math.max.apply( Math, this.colYs );
40538         var size = {
40539             height: this.maxY
40540         };
40541       
40542         if ( this.isFitWidth ) {
40543             size.width = this._getContainerFitWidth();
40544         }
40545       
40546         return size;
40547     },
40548     
40549     _getContainerFitWidth : function()
40550     {
40551         var unusedCols = 0;
40552         // count unused columns
40553         var i = this.cols;
40554         while ( --i ) {
40555           if ( this.colYs[i] !== 0 ) {
40556             break;
40557           }
40558           unusedCols++;
40559         }
40560         // fit container to columns that have been used
40561         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40562     },
40563     
40564     needsResizeLayout : function()
40565     {
40566         var previousWidth = this.containerWidth;
40567         this.getContainerWidth();
40568         return previousWidth !== this.containerWidth;
40569     }
40570  
40571 });
40572
40573  
40574
40575  /*
40576  * - LGPL
40577  *
40578  * element
40579  * 
40580  */
40581
40582 /**
40583  * @class Roo.bootstrap.MasonryBrick
40584  * @extends Roo.bootstrap.Component
40585  * Bootstrap MasonryBrick class
40586  * 
40587  * @constructor
40588  * Create a new MasonryBrick
40589  * @param {Object} config The config object
40590  */
40591
40592 Roo.bootstrap.MasonryBrick = function(config){
40593     
40594     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40595     
40596     Roo.bootstrap.MasonryBrick.register(this);
40597     
40598     this.addEvents({
40599         // raw events
40600         /**
40601          * @event click
40602          * When a MasonryBrick is clcik
40603          * @param {Roo.bootstrap.MasonryBrick} this
40604          * @param {Roo.EventObject} e
40605          */
40606         "click" : true
40607     });
40608 };
40609
40610 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40611     
40612     /**
40613      * @cfg {String} title
40614      */   
40615     title : '',
40616     /**
40617      * @cfg {String} html
40618      */   
40619     html : '',
40620     /**
40621      * @cfg {String} bgimage
40622      */   
40623     bgimage : '',
40624     /**
40625      * @cfg {String} videourl
40626      */   
40627     videourl : '',
40628     /**
40629      * @cfg {String} cls
40630      */   
40631     cls : '',
40632     /**
40633      * @cfg {String} href
40634      */   
40635     href : '',
40636     /**
40637      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40638      */   
40639     size : 'xs',
40640     
40641     /**
40642      * @cfg {String} placetitle (center|bottom)
40643      */   
40644     placetitle : '',
40645     
40646     /**
40647      * @cfg {Boolean} isFitContainer defalut true
40648      */   
40649     isFitContainer : true, 
40650     
40651     /**
40652      * @cfg {Boolean} preventDefault defalut false
40653      */   
40654     preventDefault : false, 
40655     
40656     /**
40657      * @cfg {Boolean} inverse defalut false
40658      */   
40659     maskInverse : false, 
40660     
40661     getAutoCreate : function()
40662     {
40663         if(!this.isFitContainer){
40664             return this.getSplitAutoCreate();
40665         }
40666         
40667         var cls = 'masonry-brick masonry-brick-full';
40668         
40669         if(this.href.length){
40670             cls += ' masonry-brick-link';
40671         }
40672         
40673         if(this.bgimage.length){
40674             cls += ' masonry-brick-image';
40675         }
40676         
40677         if(this.maskInverse){
40678             cls += ' mask-inverse';
40679         }
40680         
40681         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40682             cls += ' enable-mask';
40683         }
40684         
40685         if(this.size){
40686             cls += ' masonry-' + this.size + '-brick';
40687         }
40688         
40689         if(this.placetitle.length){
40690             
40691             switch (this.placetitle) {
40692                 case 'center' :
40693                     cls += ' masonry-center-title';
40694                     break;
40695                 case 'bottom' :
40696                     cls += ' masonry-bottom-title';
40697                     break;
40698                 default:
40699                     break;
40700             }
40701             
40702         } else {
40703             if(!this.html.length && !this.bgimage.length){
40704                 cls += ' masonry-center-title';
40705             }
40706
40707             if(!this.html.length && this.bgimage.length){
40708                 cls += ' masonry-bottom-title';
40709             }
40710         }
40711         
40712         if(this.cls){
40713             cls += ' ' + this.cls;
40714         }
40715         
40716         var cfg = {
40717             tag: (this.href.length) ? 'a' : 'div',
40718             cls: cls,
40719             cn: [
40720                 {
40721                     tag: 'div',
40722                     cls: 'masonry-brick-mask'
40723                 },
40724                 {
40725                     tag: 'div',
40726                     cls: 'masonry-brick-paragraph',
40727                     cn: []
40728                 }
40729             ]
40730         };
40731         
40732         if(this.href.length){
40733             cfg.href = this.href;
40734         }
40735         
40736         var cn = cfg.cn[1].cn;
40737         
40738         if(this.title.length){
40739             cn.push({
40740                 tag: 'h4',
40741                 cls: 'masonry-brick-title',
40742                 html: this.title
40743             });
40744         }
40745         
40746         if(this.html.length){
40747             cn.push({
40748                 tag: 'p',
40749                 cls: 'masonry-brick-text',
40750                 html: this.html
40751             });
40752         }
40753         
40754         if (!this.title.length && !this.html.length) {
40755             cfg.cn[1].cls += ' hide';
40756         }
40757         
40758         if(this.bgimage.length){
40759             cfg.cn.push({
40760                 tag: 'img',
40761                 cls: 'masonry-brick-image-view',
40762                 src: this.bgimage
40763             });
40764         }
40765         
40766         if(this.videourl.length){
40767             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40768             // youtube support only?
40769             cfg.cn.push({
40770                 tag: 'iframe',
40771                 cls: 'masonry-brick-image-view',
40772                 src: vurl,
40773                 frameborder : 0,
40774                 allowfullscreen : true
40775             });
40776         }
40777         
40778         return cfg;
40779         
40780     },
40781     
40782     getSplitAutoCreate : function()
40783     {
40784         var cls = 'masonry-brick masonry-brick-split';
40785         
40786         if(this.href.length){
40787             cls += ' masonry-brick-link';
40788         }
40789         
40790         if(this.bgimage.length){
40791             cls += ' masonry-brick-image';
40792         }
40793         
40794         if(this.size){
40795             cls += ' masonry-' + this.size + '-brick';
40796         }
40797         
40798         switch (this.placetitle) {
40799             case 'center' :
40800                 cls += ' masonry-center-title';
40801                 break;
40802             case 'bottom' :
40803                 cls += ' masonry-bottom-title';
40804                 break;
40805             default:
40806                 if(!this.bgimage.length){
40807                     cls += ' masonry-center-title';
40808                 }
40809
40810                 if(this.bgimage.length){
40811                     cls += ' masonry-bottom-title';
40812                 }
40813                 break;
40814         }
40815         
40816         if(this.cls){
40817             cls += ' ' + this.cls;
40818         }
40819         
40820         var cfg = {
40821             tag: (this.href.length) ? 'a' : 'div',
40822             cls: cls,
40823             cn: [
40824                 {
40825                     tag: 'div',
40826                     cls: 'masonry-brick-split-head',
40827                     cn: [
40828                         {
40829                             tag: 'div',
40830                             cls: 'masonry-brick-paragraph',
40831                             cn: []
40832                         }
40833                     ]
40834                 },
40835                 {
40836                     tag: 'div',
40837                     cls: 'masonry-brick-split-body',
40838                     cn: []
40839                 }
40840             ]
40841         };
40842         
40843         if(this.href.length){
40844             cfg.href = this.href;
40845         }
40846         
40847         if(this.title.length){
40848             cfg.cn[0].cn[0].cn.push({
40849                 tag: 'h4',
40850                 cls: 'masonry-brick-title',
40851                 html: this.title
40852             });
40853         }
40854         
40855         if(this.html.length){
40856             cfg.cn[1].cn.push({
40857                 tag: 'p',
40858                 cls: 'masonry-brick-text',
40859                 html: this.html
40860             });
40861         }
40862
40863         if(this.bgimage.length){
40864             cfg.cn[0].cn.push({
40865                 tag: 'img',
40866                 cls: 'masonry-brick-image-view',
40867                 src: this.bgimage
40868             });
40869         }
40870         
40871         if(this.videourl.length){
40872             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40873             // youtube support only?
40874             cfg.cn[0].cn.cn.push({
40875                 tag: 'iframe',
40876                 cls: 'masonry-brick-image-view',
40877                 src: vurl,
40878                 frameborder : 0,
40879                 allowfullscreen : true
40880             });
40881         }
40882         
40883         return cfg;
40884     },
40885     
40886     initEvents: function() 
40887     {
40888         switch (this.size) {
40889             case 'xs' :
40890                 this.x = 1;
40891                 this.y = 1;
40892                 break;
40893             case 'sm' :
40894                 this.x = 2;
40895                 this.y = 2;
40896                 break;
40897             case 'md' :
40898             case 'md-left' :
40899             case 'md-right' :
40900                 this.x = 3;
40901                 this.y = 3;
40902                 break;
40903             case 'tall' :
40904                 this.x = 2;
40905                 this.y = 3;
40906                 break;
40907             case 'wide' :
40908                 this.x = 3;
40909                 this.y = 2;
40910                 break;
40911             case 'wide-thin' :
40912                 this.x = 3;
40913                 this.y = 1;
40914                 break;
40915                         
40916             default :
40917                 break;
40918         }
40919         
40920         if(Roo.isTouch){
40921             this.el.on('touchstart', this.onTouchStart, this);
40922             this.el.on('touchmove', this.onTouchMove, this);
40923             this.el.on('touchend', this.onTouchEnd, this);
40924             this.el.on('contextmenu', this.onContextMenu, this);
40925         } else {
40926             this.el.on('mouseenter'  ,this.enter, this);
40927             this.el.on('mouseleave', this.leave, this);
40928             this.el.on('click', this.onClick, this);
40929         }
40930         
40931         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40932             this.parent().bricks.push(this);   
40933         }
40934         
40935     },
40936     
40937     onClick: function(e, el)
40938     {
40939         var time = this.endTimer - this.startTimer;
40940         // Roo.log(e.preventDefault());
40941         if(Roo.isTouch){
40942             if(time > 1000){
40943                 e.preventDefault();
40944                 return;
40945             }
40946         }
40947         
40948         if(!this.preventDefault){
40949             return;
40950         }
40951         
40952         e.preventDefault();
40953         
40954         if (this.activeClass != '') {
40955             this.selectBrick();
40956         }
40957         
40958         this.fireEvent('click', this, e);
40959     },
40960     
40961     enter: function(e, el)
40962     {
40963         e.preventDefault();
40964         
40965         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40966             return;
40967         }
40968         
40969         if(this.bgimage.length && this.html.length){
40970             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40971         }
40972     },
40973     
40974     leave: function(e, el)
40975     {
40976         e.preventDefault();
40977         
40978         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
40979             return;
40980         }
40981         
40982         if(this.bgimage.length && this.html.length){
40983             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40984         }
40985     },
40986     
40987     onTouchStart: function(e, el)
40988     {
40989 //        e.preventDefault();
40990         
40991         this.touchmoved = false;
40992         
40993         if(!this.isFitContainer){
40994             return;
40995         }
40996         
40997         if(!this.bgimage.length || !this.html.length){
40998             return;
40999         }
41000         
41001         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41002         
41003         this.timer = new Date().getTime();
41004         
41005     },
41006     
41007     onTouchMove: function(e, el)
41008     {
41009         this.touchmoved = true;
41010     },
41011     
41012     onContextMenu : function(e,el)
41013     {
41014         e.preventDefault();
41015         e.stopPropagation();
41016         return false;
41017     },
41018     
41019     onTouchEnd: function(e, el)
41020     {
41021 //        e.preventDefault();
41022         
41023         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
41024         
41025             this.leave(e,el);
41026             
41027             return;
41028         }
41029         
41030         if(!this.bgimage.length || !this.html.length){
41031             
41032             if(this.href.length){
41033                 window.location.href = this.href;
41034             }
41035             
41036             return;
41037         }
41038         
41039         if(!this.isFitContainer){
41040             return;
41041         }
41042         
41043         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41044         
41045         window.location.href = this.href;
41046     },
41047     
41048     //selection on single brick only
41049     selectBrick : function() {
41050         
41051         if (!this.parentId) {
41052             return;
41053         }
41054         
41055         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41056         var index = m.selectedBrick.indexOf(this.id);
41057         
41058         if ( index > -1) {
41059             m.selectedBrick.splice(index,1);
41060             this.el.removeClass(this.activeClass);
41061             return;
41062         }
41063         
41064         for(var i = 0; i < m.selectedBrick.length; i++) {
41065             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41066             b.el.removeClass(b.activeClass);
41067         }
41068         
41069         m.selectedBrick = [];
41070         
41071         m.selectedBrick.push(this.id);
41072         this.el.addClass(this.activeClass);
41073         return;
41074     },
41075     
41076     isSelected : function(){
41077         return this.el.hasClass(this.activeClass);
41078         
41079     }
41080 });
41081
41082 Roo.apply(Roo.bootstrap.MasonryBrick, {
41083     
41084     //groups: {},
41085     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41086      /**
41087     * register a Masonry Brick
41088     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41089     */
41090     
41091     register : function(brick)
41092     {
41093         //this.groups[brick.id] = brick;
41094         this.groups.add(brick.id, brick);
41095     },
41096     /**
41097     * fetch a  masonry brick based on the masonry brick ID
41098     * @param {string} the masonry brick to add
41099     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41100     */
41101     
41102     get: function(brick_id) 
41103     {
41104         // if (typeof(this.groups[brick_id]) == 'undefined') {
41105         //     return false;
41106         // }
41107         // return this.groups[brick_id] ;
41108         
41109         if(this.groups.key(brick_id)) {
41110             return this.groups.key(brick_id);
41111         }
41112         
41113         return false;
41114     }
41115     
41116     
41117     
41118 });
41119
41120  /*
41121  * - LGPL
41122  *
41123  * element
41124  * 
41125  */
41126
41127 /**
41128  * @class Roo.bootstrap.Brick
41129  * @extends Roo.bootstrap.Component
41130  * Bootstrap Brick class
41131  * 
41132  * @constructor
41133  * Create a new Brick
41134  * @param {Object} config The config object
41135  */
41136
41137 Roo.bootstrap.Brick = function(config){
41138     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41139     
41140     this.addEvents({
41141         // raw events
41142         /**
41143          * @event click
41144          * When a Brick is click
41145          * @param {Roo.bootstrap.Brick} this
41146          * @param {Roo.EventObject} e
41147          */
41148         "click" : true
41149     });
41150 };
41151
41152 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
41153     
41154     /**
41155      * @cfg {String} title
41156      */   
41157     title : '',
41158     /**
41159      * @cfg {String} html
41160      */   
41161     html : '',
41162     /**
41163      * @cfg {String} bgimage
41164      */   
41165     bgimage : '',
41166     /**
41167      * @cfg {String} cls
41168      */   
41169     cls : '',
41170     /**
41171      * @cfg {String} href
41172      */   
41173     href : '',
41174     /**
41175      * @cfg {String} video
41176      */   
41177     video : '',
41178     /**
41179      * @cfg {Boolean} square
41180      */   
41181     square : true,
41182     
41183     getAutoCreate : function()
41184     {
41185         var cls = 'roo-brick';
41186         
41187         if(this.href.length){
41188             cls += ' roo-brick-link';
41189         }
41190         
41191         if(this.bgimage.length){
41192             cls += ' roo-brick-image';
41193         }
41194         
41195         if(!this.html.length && !this.bgimage.length){
41196             cls += ' roo-brick-center-title';
41197         }
41198         
41199         if(!this.html.length && this.bgimage.length){
41200             cls += ' roo-brick-bottom-title';
41201         }
41202         
41203         if(this.cls){
41204             cls += ' ' + this.cls;
41205         }
41206         
41207         var cfg = {
41208             tag: (this.href.length) ? 'a' : 'div',
41209             cls: cls,
41210             cn: [
41211                 {
41212                     tag: 'div',
41213                     cls: 'roo-brick-paragraph',
41214                     cn: []
41215                 }
41216             ]
41217         };
41218         
41219         if(this.href.length){
41220             cfg.href = this.href;
41221         }
41222         
41223         var cn = cfg.cn[0].cn;
41224         
41225         if(this.title.length){
41226             cn.push({
41227                 tag: 'h4',
41228                 cls: 'roo-brick-title',
41229                 html: this.title
41230             });
41231         }
41232         
41233         if(this.html.length){
41234             cn.push({
41235                 tag: 'p',
41236                 cls: 'roo-brick-text',
41237                 html: this.html
41238             });
41239         } else {
41240             cn.cls += ' hide';
41241         }
41242         
41243         if(this.bgimage.length){
41244             cfg.cn.push({
41245                 tag: 'img',
41246                 cls: 'roo-brick-image-view',
41247                 src: this.bgimage
41248             });
41249         }
41250         
41251         return cfg;
41252     },
41253     
41254     initEvents: function() 
41255     {
41256         if(this.title.length || this.html.length){
41257             this.el.on('mouseenter'  ,this.enter, this);
41258             this.el.on('mouseleave', this.leave, this);
41259         }
41260         
41261         Roo.EventManager.onWindowResize(this.resize, this); 
41262         
41263         if(this.bgimage.length){
41264             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41265             this.imageEl.on('load', this.onImageLoad, this);
41266             return;
41267         }
41268         
41269         this.resize();
41270     },
41271     
41272     onImageLoad : function()
41273     {
41274         this.resize();
41275     },
41276     
41277     resize : function()
41278     {
41279         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41280         
41281         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41282         
41283         if(this.bgimage.length){
41284             var image = this.el.select('.roo-brick-image-view', true).first();
41285             
41286             image.setWidth(paragraph.getWidth());
41287             
41288             if(this.square){
41289                 image.setHeight(paragraph.getWidth());
41290             }
41291             
41292             this.el.setHeight(image.getHeight());
41293             paragraph.setHeight(image.getHeight());
41294             
41295         }
41296         
41297     },
41298     
41299     enter: function(e, el)
41300     {
41301         e.preventDefault();
41302         
41303         if(this.bgimage.length){
41304             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41305             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41306         }
41307     },
41308     
41309     leave: function(e, el)
41310     {
41311         e.preventDefault();
41312         
41313         if(this.bgimage.length){
41314             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41315             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41316         }
41317     }
41318     
41319 });
41320
41321  
41322
41323  /*
41324  * - LGPL
41325  *
41326  * Number field 
41327  */
41328
41329 /**
41330  * @class Roo.bootstrap.form.NumberField
41331  * @extends Roo.bootstrap.form.Input
41332  * Bootstrap NumberField class
41333  * 
41334  * 
41335  * 
41336  * 
41337  * @constructor
41338  * Create a new NumberField
41339  * @param {Object} config The config object
41340  */
41341
41342 Roo.bootstrap.form.NumberField = function(config){
41343     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41344 };
41345
41346 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41347     
41348     /**
41349      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41350      */
41351     allowDecimals : true,
41352     /**
41353      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41354      */
41355     decimalSeparator : ".",
41356     /**
41357      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41358      */
41359     decimalPrecision : 2,
41360     /**
41361      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41362      */
41363     allowNegative : true,
41364     
41365     /**
41366      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41367      */
41368     allowZero: true,
41369     /**
41370      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41371      */
41372     minValue : Number.NEGATIVE_INFINITY,
41373     /**
41374      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41375      */
41376     maxValue : Number.MAX_VALUE,
41377     /**
41378      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41379      */
41380     minText : "The minimum value for this field is {0}",
41381     /**
41382      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41383      */
41384     maxText : "The maximum value for this field is {0}",
41385     /**
41386      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41387      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41388      */
41389     nanText : "{0} is not a valid number",
41390     /**
41391      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41392      */
41393     thousandsDelimiter : false,
41394     /**
41395      * @cfg {String} valueAlign alignment of value
41396      */
41397     valueAlign : "left",
41398
41399     getAutoCreate : function()
41400     {
41401         var hiddenInput = {
41402             tag: 'input',
41403             type: 'hidden',
41404             id: Roo.id(),
41405             cls: 'hidden-number-input'
41406         };
41407         
41408         if (this.name) {
41409             hiddenInput.name = this.name;
41410         }
41411         
41412         this.name = '';
41413         
41414         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41415         
41416         this.name = hiddenInput.name;
41417         
41418         if(cfg.cn.length > 0) {
41419             cfg.cn.push(hiddenInput);
41420         }
41421         
41422         return cfg;
41423     },
41424
41425     // private
41426     initEvents : function()
41427     {   
41428         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41429         
41430         var allowed = "0123456789";
41431         
41432         if(this.allowDecimals){
41433             allowed += this.decimalSeparator;
41434         }
41435         
41436         if(this.allowNegative){
41437             allowed += "-";
41438         }
41439         
41440         if(this.thousandsDelimiter) {
41441             allowed += ",";
41442         }
41443         
41444         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41445         
41446         var keyPress = function(e){
41447             
41448             var k = e.getKey();
41449             
41450             var c = e.getCharCode();
41451             
41452             if(
41453                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41454                     allowed.indexOf(String.fromCharCode(c)) === -1
41455             ){
41456                 e.stopEvent();
41457                 return;
41458             }
41459             
41460             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41461                 return;
41462             }
41463             
41464             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41465                 e.stopEvent();
41466             }
41467         };
41468         
41469         this.el.on("keypress", keyPress, this);
41470     },
41471     
41472     validateValue : function(value)
41473     {
41474         
41475         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41476             return false;
41477         }
41478         
41479         var num = this.parseValue(value);
41480         
41481         if(isNaN(num)){
41482             this.markInvalid(String.format(this.nanText, value));
41483             return false;
41484         }
41485         
41486         if(num < this.minValue){
41487             this.markInvalid(String.format(this.minText, this.minValue));
41488             return false;
41489         }
41490         
41491         if(num > this.maxValue){
41492             this.markInvalid(String.format(this.maxText, this.maxValue));
41493             return false;
41494         }
41495         
41496         return true;
41497     },
41498
41499     getValue : function()
41500     {
41501         var v = this.hiddenEl().getValue();
41502         
41503         return this.fixPrecision(this.parseValue(v));
41504     },
41505
41506     parseValue : function(value)
41507     {
41508         if(this.thousandsDelimiter) {
41509             value += "";
41510             r = new RegExp(",", "g");
41511             value = value.replace(r, "");
41512         }
41513         
41514         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41515         return isNaN(value) ? '' : value;
41516     },
41517
41518     fixPrecision : function(value)
41519     {
41520         if(this.thousandsDelimiter) {
41521             value += "";
41522             r = new RegExp(",", "g");
41523             value = value.replace(r, "");
41524         }
41525         
41526         var nan = isNaN(value);
41527         
41528         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41529             return nan ? '' : value;
41530         }
41531         return parseFloat(value).toFixed(this.decimalPrecision);
41532     },
41533
41534     setValue : function(v)
41535     {
41536         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41537         
41538         this.value = v;
41539         
41540         if(this.rendered){
41541             
41542             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41543             
41544             this.inputEl().dom.value = (v == '') ? '' :
41545                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41546             
41547             if(!this.allowZero && v === '0') {
41548                 this.hiddenEl().dom.value = '';
41549                 this.inputEl().dom.value = '';
41550             }
41551             
41552             this.validate();
41553         }
41554     },
41555
41556     decimalPrecisionFcn : function(v)
41557     {
41558         return Math.floor(v);
41559     },
41560
41561     beforeBlur : function()
41562     {
41563         var v = this.parseValue(this.getRawValue());
41564         
41565         if(v || v === 0 || v === ''){
41566             this.setValue(v);
41567         }
41568     },
41569     
41570     hiddenEl : function()
41571     {
41572         return this.el.select('input.hidden-number-input',true).first();
41573     }
41574     
41575 });
41576
41577  
41578
41579 /*
41580 * Licence: LGPL
41581 */
41582
41583 /**
41584  * @class Roo.bootstrap.DocumentSlider
41585  * @extends Roo.bootstrap.Component
41586  * Bootstrap DocumentSlider class
41587  * 
41588  * @constructor
41589  * Create a new DocumentViewer
41590  * @param {Object} config The config object
41591  */
41592
41593 Roo.bootstrap.DocumentSlider = function(config){
41594     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41595     
41596     this.files = [];
41597     
41598     this.addEvents({
41599         /**
41600          * @event initial
41601          * Fire after initEvent
41602          * @param {Roo.bootstrap.DocumentSlider} this
41603          */
41604         "initial" : true,
41605         /**
41606          * @event update
41607          * Fire after update
41608          * @param {Roo.bootstrap.DocumentSlider} this
41609          */
41610         "update" : true,
41611         /**
41612          * @event click
41613          * Fire after click
41614          * @param {Roo.bootstrap.DocumentSlider} this
41615          */
41616         "click" : true
41617     });
41618 };
41619
41620 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41621     
41622     files : false,
41623     
41624     indicator : 0,
41625     
41626     getAutoCreate : function()
41627     {
41628         var cfg = {
41629             tag : 'div',
41630             cls : 'roo-document-slider',
41631             cn : [
41632                 {
41633                     tag : 'div',
41634                     cls : 'roo-document-slider-header',
41635                     cn : [
41636                         {
41637                             tag : 'div',
41638                             cls : 'roo-document-slider-header-title'
41639                         }
41640                     ]
41641                 },
41642                 {
41643                     tag : 'div',
41644                     cls : 'roo-document-slider-body',
41645                     cn : [
41646                         {
41647                             tag : 'div',
41648                             cls : 'roo-document-slider-prev',
41649                             cn : [
41650                                 {
41651                                     tag : 'i',
41652                                     cls : 'fa fa-chevron-left'
41653                                 }
41654                             ]
41655                         },
41656                         {
41657                             tag : 'div',
41658                             cls : 'roo-document-slider-thumb',
41659                             cn : [
41660                                 {
41661                                     tag : 'img',
41662                                     cls : 'roo-document-slider-image'
41663                                 }
41664                             ]
41665                         },
41666                         {
41667                             tag : 'div',
41668                             cls : 'roo-document-slider-next',
41669                             cn : [
41670                                 {
41671                                     tag : 'i',
41672                                     cls : 'fa fa-chevron-right'
41673                                 }
41674                             ]
41675                         }
41676                     ]
41677                 }
41678             ]
41679         };
41680         
41681         return cfg;
41682     },
41683     
41684     initEvents : function()
41685     {
41686         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41687         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41688         
41689         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41690         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41691         
41692         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41693         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41694         
41695         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41696         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41697         
41698         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41699         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41700         
41701         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41702         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41703         
41704         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41705         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41706         
41707         this.thumbEl.on('click', this.onClick, this);
41708         
41709         this.prevIndicator.on('click', this.prev, this);
41710         
41711         this.nextIndicator.on('click', this.next, this);
41712         
41713     },
41714     
41715     initial : function()
41716     {
41717         if(this.files.length){
41718             this.indicator = 1;
41719             this.update()
41720         }
41721         
41722         this.fireEvent('initial', this);
41723     },
41724     
41725     update : function()
41726     {
41727         this.imageEl.attr('src', this.files[this.indicator - 1]);
41728         
41729         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41730         
41731         this.prevIndicator.show();
41732         
41733         if(this.indicator == 1){
41734             this.prevIndicator.hide();
41735         }
41736         
41737         this.nextIndicator.show();
41738         
41739         if(this.indicator == this.files.length){
41740             this.nextIndicator.hide();
41741         }
41742         
41743         this.thumbEl.scrollTo('top');
41744         
41745         this.fireEvent('update', this);
41746     },
41747     
41748     onClick : function(e)
41749     {
41750         e.preventDefault();
41751         
41752         this.fireEvent('click', this);
41753     },
41754     
41755     prev : function(e)
41756     {
41757         e.preventDefault();
41758         
41759         this.indicator = Math.max(1, this.indicator - 1);
41760         
41761         this.update();
41762     },
41763     
41764     next : function(e)
41765     {
41766         e.preventDefault();
41767         
41768         this.indicator = Math.min(this.files.length, this.indicator + 1);
41769         
41770         this.update();
41771     }
41772 });
41773 /*
41774  * - LGPL
41775  *
41776  * RadioSet
41777  *
41778  *
41779  */
41780
41781 /**
41782  * @class Roo.bootstrap.form.RadioSet
41783  * @extends Roo.bootstrap.form.Input
41784  * @children Roo.bootstrap.form.Radio
41785  * Bootstrap RadioSet class
41786  * @cfg {String} indicatorpos (left|right) default left
41787  * @cfg {Boolean} inline (true|false) inline the element (default true)
41788  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41789  * @constructor
41790  * Create a new RadioSet
41791  * @param {Object} config The config object
41792  */
41793
41794 Roo.bootstrap.form.RadioSet = function(config){
41795     
41796     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41797     
41798     this.radioes = [];
41799     
41800     Roo.bootstrap.form.RadioSet.register(this);
41801     
41802     this.addEvents({
41803         /**
41804         * @event check
41805         * Fires when the element is checked or unchecked.
41806         * @param {Roo.bootstrap.form.RadioSet} this This radio
41807         * @param {Roo.bootstrap.form.Radio} item The checked item
41808         */
41809        check : true,
41810        /**
41811         * @event click
41812         * Fires when the element is click.
41813         * @param {Roo.bootstrap.form.RadioSet} this This radio set
41814         * @param {Roo.bootstrap.form.Radio} item The checked item
41815         * @param {Roo.EventObject} e The event object
41816         */
41817        click : true
41818     });
41819     
41820 };
41821
41822 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
41823
41824     radioes : false,
41825     
41826     inline : true,
41827     
41828     weight : '',
41829     
41830     indicatorpos : 'left',
41831     
41832     getAutoCreate : function()
41833     {
41834         var label = {
41835             tag : 'label',
41836             cls : 'roo-radio-set-label',
41837             cn : [
41838                 {
41839                     tag : 'span',
41840                     html : this.fieldLabel
41841                 }
41842             ]
41843         };
41844         if (Roo.bootstrap.version == 3) {
41845             
41846             
41847             if(this.indicatorpos == 'left'){
41848                 label.cn.unshift({
41849                     tag : 'i',
41850                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41851                     tooltip : 'This field is required'
41852                 });
41853             } else {
41854                 label.cn.push({
41855                     tag : 'i',
41856                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41857                     tooltip : 'This field is required'
41858                 });
41859             }
41860         }
41861         var items = {
41862             tag : 'div',
41863             cls : 'roo-radio-set-items'
41864         };
41865         
41866         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41867         
41868         if (align === 'left' && this.fieldLabel.length) {
41869             
41870             items = {
41871                 cls : "roo-radio-set-right", 
41872                 cn: [
41873                     items
41874                 ]
41875             };
41876             
41877             if(this.labelWidth > 12){
41878                 label.style = "width: " + this.labelWidth + 'px';
41879             }
41880             
41881             if(this.labelWidth < 13 && this.labelmd == 0){
41882                 this.labelmd = this.labelWidth;
41883             }
41884             
41885             if(this.labellg > 0){
41886                 label.cls += ' col-lg-' + this.labellg;
41887                 items.cls += ' col-lg-' + (12 - this.labellg);
41888             }
41889             
41890             if(this.labelmd > 0){
41891                 label.cls += ' col-md-' + this.labelmd;
41892                 items.cls += ' col-md-' + (12 - this.labelmd);
41893             }
41894             
41895             if(this.labelsm > 0){
41896                 label.cls += ' col-sm-' + this.labelsm;
41897                 items.cls += ' col-sm-' + (12 - this.labelsm);
41898             }
41899             
41900             if(this.labelxs > 0){
41901                 label.cls += ' col-xs-' + this.labelxs;
41902                 items.cls += ' col-xs-' + (12 - this.labelxs);
41903             }
41904         }
41905         
41906         var cfg = {
41907             tag : 'div',
41908             cls : 'roo-radio-set',
41909             cn : [
41910                 {
41911                     tag : 'input',
41912                     cls : 'roo-radio-set-input',
41913                     type : 'hidden',
41914                     name : this.name,
41915                     value : this.value ? this.value :  ''
41916                 },
41917                 label,
41918                 items
41919             ]
41920         };
41921         
41922         if(this.weight.length){
41923             cfg.cls += ' roo-radio-' + this.weight;
41924         }
41925         
41926         if(this.inline) {
41927             cfg.cls += ' roo-radio-set-inline';
41928         }
41929         
41930         var settings=this;
41931         ['xs','sm','md','lg'].map(function(size){
41932             if (settings[size]) {
41933                 cfg.cls += ' col-' + size + '-' + settings[size];
41934             }
41935         });
41936         
41937         return cfg;
41938         
41939     },
41940
41941     initEvents : function()
41942     {
41943         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41944         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41945         
41946         if(!this.fieldLabel.length){
41947             this.labelEl.hide();
41948         }
41949         
41950         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41951         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41952         
41953         this.indicator = this.indicatorEl();
41954         
41955         if(this.indicator){
41956             this.indicator.addClass('invisible');
41957         }
41958         
41959         this.originalValue = this.getValue();
41960         
41961     },
41962     
41963     inputEl: function ()
41964     {
41965         return this.el.select('.roo-radio-set-input', true).first();
41966     },
41967     
41968     getChildContainer : function()
41969     {
41970         return this.itemsEl;
41971     },
41972     
41973     register : function(item)
41974     {
41975         this.radioes.push(item);
41976         
41977     },
41978     
41979     validate : function()
41980     {   
41981         if(this.getVisibilityEl().hasClass('hidden')){
41982             return true;
41983         }
41984         
41985         var valid = false;
41986         
41987         Roo.each(this.radioes, function(i){
41988             if(!i.checked){
41989                 return;
41990             }
41991             
41992             valid = true;
41993             return false;
41994         });
41995         
41996         if(this.allowBlank) {
41997             return true;
41998         }
41999         
42000         if(this.disabled || valid){
42001             this.markValid();
42002             return true;
42003         }
42004         
42005         this.markInvalid();
42006         return false;
42007         
42008     },
42009     
42010     markValid : function()
42011     {
42012         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42013             this.indicatorEl().removeClass('visible');
42014             this.indicatorEl().addClass('invisible');
42015         }
42016         
42017         
42018         if (Roo.bootstrap.version == 3) {
42019             this.el.removeClass([this.invalidClass, this.validClass]);
42020             this.el.addClass(this.validClass);
42021         } else {
42022             this.el.removeClass(['is-invalid','is-valid']);
42023             this.el.addClass(['is-valid']);
42024         }
42025         this.fireEvent('valid', this);
42026     },
42027     
42028     markInvalid : function(msg)
42029     {
42030         if(this.allowBlank || this.disabled){
42031             return;
42032         }
42033         
42034         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42035             this.indicatorEl().removeClass('invisible');
42036             this.indicatorEl().addClass('visible');
42037         }
42038         if (Roo.bootstrap.version == 3) {
42039             this.el.removeClass([this.invalidClass, this.validClass]);
42040             this.el.addClass(this.invalidClass);
42041         } else {
42042             this.el.removeClass(['is-invalid','is-valid']);
42043             this.el.addClass(['is-invalid']);
42044         }
42045         
42046         this.fireEvent('invalid', this, msg);
42047         
42048     },
42049     
42050     setValue : function(v, suppressEvent)
42051     {   
42052         if(this.value === v){
42053             return;
42054         }
42055         
42056         this.value = v;
42057         
42058         if(this.rendered){
42059             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42060         }
42061         
42062         Roo.each(this.radioes, function(i){
42063             i.checked = false;
42064             i.el.removeClass('checked');
42065         });
42066         
42067         Roo.each(this.radioes, function(i){
42068             
42069             if(i.value === v || i.value.toString() === v.toString()){
42070                 i.checked = true;
42071                 i.el.addClass('checked');
42072                 
42073                 if(suppressEvent !== true){
42074                     this.fireEvent('check', this, i);
42075                 }
42076                 
42077                 return false;
42078             }
42079             
42080         }, this);
42081         
42082         this.validate();
42083     },
42084     
42085     clearInvalid : function(){
42086         
42087         if(!this.el || this.preventMark){
42088             return;
42089         }
42090         
42091         this.el.removeClass([this.invalidClass]);
42092         
42093         this.fireEvent('valid', this);
42094     }
42095     
42096 });
42097
42098 Roo.apply(Roo.bootstrap.form.RadioSet, {
42099     
42100     groups: {},
42101     
42102     register : function(set)
42103     {
42104         this.groups[set.name] = set;
42105     },
42106     
42107     get: function(name) 
42108     {
42109         if (typeof(this.groups[name]) == 'undefined') {
42110             return false;
42111         }
42112         
42113         return this.groups[name] ;
42114     }
42115     
42116 });
42117 /*
42118  * Based on:
42119  * Ext JS Library 1.1.1
42120  * Copyright(c) 2006-2007, Ext JS, LLC.
42121  *
42122  * Originally Released Under LGPL - original licence link has changed is not relivant.
42123  *
42124  * Fork - LGPL
42125  * <script type="text/javascript">
42126  */
42127
42128
42129 /**
42130  * @class Roo.bootstrap.SplitBar
42131  * @extends Roo.util.Observable
42132  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42133  * <br><br>
42134  * Usage:
42135  * <pre><code>
42136 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42137                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42138 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42139 split.minSize = 100;
42140 split.maxSize = 600;
42141 split.animate = true;
42142 split.on('moved', splitterMoved);
42143 </code></pre>
42144  * @constructor
42145  * Create a new SplitBar
42146  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
42147  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
42148  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42149  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
42150                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42151                         position of the SplitBar).
42152  */
42153 Roo.bootstrap.SplitBar = function(cfg){
42154     
42155     /** @private */
42156     
42157     //{
42158     //  dragElement : elm
42159     //  resizingElement: el,
42160         // optional..
42161     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42162     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
42163         // existingProxy ???
42164     //}
42165     
42166     this.el = Roo.get(cfg.dragElement, true);
42167     this.el.dom.unselectable = "on";
42168     /** @private */
42169     this.resizingEl = Roo.get(cfg.resizingElement, true);
42170
42171     /**
42172      * @private
42173      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42174      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42175      * @type Number
42176      */
42177     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42178     
42179     /**
42180      * The minimum size of the resizing element. (Defaults to 0)
42181      * @type Number
42182      */
42183     this.minSize = 0;
42184     
42185     /**
42186      * The maximum size of the resizing element. (Defaults to 2000)
42187      * @type Number
42188      */
42189     this.maxSize = 2000;
42190     
42191     /**
42192      * Whether to animate the transition to the new size
42193      * @type Boolean
42194      */
42195     this.animate = false;
42196     
42197     /**
42198      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42199      * @type Boolean
42200      */
42201     this.useShim = false;
42202     
42203     /** @private */
42204     this.shim = null;
42205     
42206     if(!cfg.existingProxy){
42207         /** @private */
42208         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42209     }else{
42210         this.proxy = Roo.get(cfg.existingProxy).dom;
42211     }
42212     /** @private */
42213     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42214     
42215     /** @private */
42216     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42217     
42218     /** @private */
42219     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42220     
42221     /** @private */
42222     this.dragSpecs = {};
42223     
42224     /**
42225      * @private The adapter to use to positon and resize elements
42226      */
42227     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42228     this.adapter.init(this);
42229     
42230     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42231         /** @private */
42232         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42233         this.el.addClass("roo-splitbar-h");
42234     }else{
42235         /** @private */
42236         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42237         this.el.addClass("roo-splitbar-v");
42238     }
42239     
42240     this.addEvents({
42241         /**
42242          * @event resize
42243          * Fires when the splitter is moved (alias for {@link #event-moved})
42244          * @param {Roo.bootstrap.SplitBar} this
42245          * @param {Number} newSize the new width or height
42246          */
42247         "resize" : true,
42248         /**
42249          * @event moved
42250          * Fires when the splitter is moved
42251          * @param {Roo.bootstrap.SplitBar} this
42252          * @param {Number} newSize the new width or height
42253          */
42254         "moved" : true,
42255         /**
42256          * @event beforeresize
42257          * Fires before the splitter is dragged
42258          * @param {Roo.bootstrap.SplitBar} this
42259          */
42260         "beforeresize" : true,
42261
42262         "beforeapply" : true
42263     });
42264
42265     Roo.util.Observable.call(this);
42266 };
42267
42268 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42269     onStartProxyDrag : function(x, y){
42270         this.fireEvent("beforeresize", this);
42271         if(!this.overlay){
42272             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
42273             o.unselectable();
42274             o.enableDisplayMode("block");
42275             // all splitbars share the same overlay
42276             Roo.bootstrap.SplitBar.prototype.overlay = o;
42277         }
42278         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42279         this.overlay.show();
42280         Roo.get(this.proxy).setDisplayed("block");
42281         var size = this.adapter.getElementSize(this);
42282         this.activeMinSize = this.getMinimumSize();;
42283         this.activeMaxSize = this.getMaximumSize();;
42284         var c1 = size - this.activeMinSize;
42285         var c2 = Math.max(this.activeMaxSize - size, 0);
42286         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42287             this.dd.resetConstraints();
42288             this.dd.setXConstraint(
42289                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
42290                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42291             );
42292             this.dd.setYConstraint(0, 0);
42293         }else{
42294             this.dd.resetConstraints();
42295             this.dd.setXConstraint(0, 0);
42296             this.dd.setYConstraint(
42297                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
42298                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42299             );
42300          }
42301         this.dragSpecs.startSize = size;
42302         this.dragSpecs.startPoint = [x, y];
42303         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42304     },
42305     
42306     /** 
42307      * @private Called after the drag operation by the DDProxy
42308      */
42309     onEndProxyDrag : function(e){
42310         Roo.get(this.proxy).setDisplayed(false);
42311         var endPoint = Roo.lib.Event.getXY(e);
42312         if(this.overlay){
42313             this.overlay.hide();
42314         }
42315         var newSize;
42316         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42317             newSize = this.dragSpecs.startSize + 
42318                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42319                     endPoint[0] - this.dragSpecs.startPoint[0] :
42320                     this.dragSpecs.startPoint[0] - endPoint[0]
42321                 );
42322         }else{
42323             newSize = this.dragSpecs.startSize + 
42324                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42325                     endPoint[1] - this.dragSpecs.startPoint[1] :
42326                     this.dragSpecs.startPoint[1] - endPoint[1]
42327                 );
42328         }
42329         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42330         if(newSize != this.dragSpecs.startSize){
42331             if(this.fireEvent('beforeapply', this, newSize) !== false){
42332                 this.adapter.setElementSize(this, newSize);
42333                 this.fireEvent("moved", this, newSize);
42334                 this.fireEvent("resize", this, newSize);
42335             }
42336         }
42337     },
42338     
42339     /**
42340      * Get the adapter this SplitBar uses
42341      * @return The adapter object
42342      */
42343     getAdapter : function(){
42344         return this.adapter;
42345     },
42346     
42347     /**
42348      * Set the adapter this SplitBar uses
42349      * @param {Object} adapter A SplitBar adapter object
42350      */
42351     setAdapter : function(adapter){
42352         this.adapter = adapter;
42353         this.adapter.init(this);
42354     },
42355     
42356     /**
42357      * Gets the minimum size for the resizing element
42358      * @return {Number} The minimum size
42359      */
42360     getMinimumSize : function(){
42361         return this.minSize;
42362     },
42363     
42364     /**
42365      * Sets the minimum size for the resizing element
42366      * @param {Number} minSize The minimum size
42367      */
42368     setMinimumSize : function(minSize){
42369         this.minSize = minSize;
42370     },
42371     
42372     /**
42373      * Gets the maximum size for the resizing element
42374      * @return {Number} The maximum size
42375      */
42376     getMaximumSize : function(){
42377         return this.maxSize;
42378     },
42379     
42380     /**
42381      * Sets the maximum size for the resizing element
42382      * @param {Number} maxSize The maximum size
42383      */
42384     setMaximumSize : function(maxSize){
42385         this.maxSize = maxSize;
42386     },
42387     
42388     /**
42389      * Sets the initialize size for the resizing element
42390      * @param {Number} size The initial size
42391      */
42392     setCurrentSize : function(size){
42393         var oldAnimate = this.animate;
42394         this.animate = false;
42395         this.adapter.setElementSize(this, size);
42396         this.animate = oldAnimate;
42397     },
42398     
42399     /**
42400      * Destroy this splitbar. 
42401      * @param {Boolean} removeEl True to remove the element
42402      */
42403     destroy : function(removeEl){
42404         if(this.shim){
42405             this.shim.remove();
42406         }
42407         this.dd.unreg();
42408         this.proxy.parentNode.removeChild(this.proxy);
42409         if(removeEl){
42410             this.el.remove();
42411         }
42412     }
42413 });
42414
42415 /**
42416  * @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.
42417  */
42418 Roo.bootstrap.SplitBar.createProxy = function(dir){
42419     var proxy = new Roo.Element(document.createElement("div"));
42420     proxy.unselectable();
42421     var cls = 'roo-splitbar-proxy';
42422     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42423     document.body.appendChild(proxy.dom);
42424     return proxy.dom;
42425 };
42426
42427 /** 
42428  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42429  * Default Adapter. It assumes the splitter and resizing element are not positioned
42430  * elements and only gets/sets the width of the element. Generally used for table based layouts.
42431  */
42432 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42433 };
42434
42435 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42436     // do nothing for now
42437     init : function(s){
42438     
42439     },
42440     /**
42441      * Called before drag operations to get the current size of the resizing element. 
42442      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42443      */
42444      getElementSize : function(s){
42445         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42446             return s.resizingEl.getWidth();
42447         }else{
42448             return s.resizingEl.getHeight();
42449         }
42450     },
42451     
42452     /**
42453      * Called after drag operations to set the size of the resizing element.
42454      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42455      * @param {Number} newSize The new size to set
42456      * @param {Function} onComplete A function to be invoked when resizing is complete
42457      */
42458     setElementSize : function(s, newSize, onComplete){
42459         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42460             if(!s.animate){
42461                 s.resizingEl.setWidth(newSize);
42462                 if(onComplete){
42463                     onComplete(s, newSize);
42464                 }
42465             }else{
42466                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42467             }
42468         }else{
42469             
42470             if(!s.animate){
42471                 s.resizingEl.setHeight(newSize);
42472                 if(onComplete){
42473                     onComplete(s, newSize);
42474                 }
42475             }else{
42476                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42477             }
42478         }
42479     }
42480 };
42481
42482 /** 
42483  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42484  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42485  * Adapter that  moves the splitter element to align with the resized sizing element. 
42486  * Used with an absolute positioned SplitBar.
42487  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42488  * document.body, make sure you assign an id to the body element.
42489  */
42490 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42491     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42492     this.container = Roo.get(container);
42493 };
42494
42495 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42496     init : function(s){
42497         this.basic.init(s);
42498     },
42499     
42500     getElementSize : function(s){
42501         return this.basic.getElementSize(s);
42502     },
42503     
42504     setElementSize : function(s, newSize, onComplete){
42505         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42506     },
42507     
42508     moveSplitter : function(s){
42509         var yes = Roo.bootstrap.SplitBar;
42510         switch(s.placement){
42511             case yes.LEFT:
42512                 s.el.setX(s.resizingEl.getRight());
42513                 break;
42514             case yes.RIGHT:
42515                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42516                 break;
42517             case yes.TOP:
42518                 s.el.setY(s.resizingEl.getBottom());
42519                 break;
42520             case yes.BOTTOM:
42521                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42522                 break;
42523         }
42524     }
42525 };
42526
42527 /**
42528  * Orientation constant - Create a vertical SplitBar
42529  * @static
42530  * @type Number
42531  */
42532 Roo.bootstrap.SplitBar.VERTICAL = 1;
42533
42534 /**
42535  * Orientation constant - Create a horizontal SplitBar
42536  * @static
42537  * @type Number
42538  */
42539 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42540
42541 /**
42542  * Placement constant - The resizing element is to the left of the splitter element
42543  * @static
42544  * @type Number
42545  */
42546 Roo.bootstrap.SplitBar.LEFT = 1;
42547
42548 /**
42549  * Placement constant - The resizing element is to the right of the splitter element
42550  * @static
42551  * @type Number
42552  */
42553 Roo.bootstrap.SplitBar.RIGHT = 2;
42554
42555 /**
42556  * Placement constant - The resizing element is positioned above the splitter element
42557  * @static
42558  * @type Number
42559  */
42560 Roo.bootstrap.SplitBar.TOP = 3;
42561
42562 /**
42563  * Placement constant - The resizing element is positioned under splitter element
42564  * @static
42565  * @type Number
42566  */
42567 Roo.bootstrap.SplitBar.BOTTOM = 4;
42568 /*
42569  * Based on:
42570  * Ext JS Library 1.1.1
42571  * Copyright(c) 2006-2007, Ext JS, LLC.
42572  *
42573  * Originally Released Under LGPL - original licence link has changed is not relivant.
42574  *
42575  * Fork - LGPL
42576  * <script type="text/javascript">
42577  */
42578
42579 /**
42580  * @class Roo.bootstrap.layout.Manager
42581  * @extends Roo.bootstrap.Component
42582  * @abstract
42583  * Base class for layout managers.
42584  */
42585 Roo.bootstrap.layout.Manager = function(config)
42586 {
42587     this.monitorWindowResize = true; // do this before we apply configuration.
42588     
42589     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42590
42591
42592
42593
42594
42595     /** false to disable window resize monitoring @type Boolean */
42596     
42597     this.regions = {};
42598     this.addEvents({
42599         /**
42600          * @event layout
42601          * Fires when a layout is performed.
42602          * @param {Roo.LayoutManager} this
42603          */
42604         "layout" : true,
42605         /**
42606          * @event regionresized
42607          * Fires when the user resizes a region.
42608          * @param {Roo.LayoutRegion} region The resized region
42609          * @param {Number} newSize The new size (width for east/west, height for north/south)
42610          */
42611         "regionresized" : true,
42612         /**
42613          * @event regioncollapsed
42614          * Fires when a region is collapsed.
42615          * @param {Roo.LayoutRegion} region The collapsed region
42616          */
42617         "regioncollapsed" : true,
42618         /**
42619          * @event regionexpanded
42620          * Fires when a region is expanded.
42621          * @param {Roo.LayoutRegion} region The expanded region
42622          */
42623         "regionexpanded" : true
42624     });
42625     this.updating = false;
42626
42627     if (config.el) {
42628         this.el = Roo.get(config.el);
42629         this.initEvents();
42630     }
42631
42632 };
42633
42634 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42635
42636
42637     regions : null,
42638
42639     monitorWindowResize : true,
42640
42641
42642     updating : false,
42643
42644
42645     onRender : function(ct, position)
42646     {
42647         if(!this.el){
42648             this.el = Roo.get(ct);
42649             this.initEvents();
42650         }
42651         //this.fireEvent('render',this);
42652     },
42653
42654
42655     initEvents: function()
42656     {
42657
42658
42659         // ie scrollbar fix
42660         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42661             document.body.scroll = "no";
42662         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42663             this.el.position('relative');
42664         }
42665         this.id = this.el.id;
42666         this.el.addClass("roo-layout-container");
42667         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42668         if(this.el.dom != document.body ) {
42669             this.el.on('resize', this.layout,this);
42670             this.el.on('show', this.layout,this);
42671         }
42672
42673     },
42674
42675     /**
42676      * Returns true if this layout is currently being updated
42677      * @return {Boolean}
42678      */
42679     isUpdating : function(){
42680         return this.updating;
42681     },
42682
42683     /**
42684      * Suspend the LayoutManager from doing auto-layouts while
42685      * making multiple add or remove calls
42686      */
42687     beginUpdate : function(){
42688         this.updating = true;
42689     },
42690
42691     /**
42692      * Restore auto-layouts and optionally disable the manager from performing a layout
42693      * @param {Boolean} noLayout true to disable a layout update
42694      */
42695     endUpdate : function(noLayout){
42696         this.updating = false;
42697         if(!noLayout){
42698             this.layout();
42699         }
42700     },
42701
42702     layout: function(){
42703         // abstract...
42704     },
42705
42706     onRegionResized : function(region, newSize){
42707         this.fireEvent("regionresized", region, newSize);
42708         this.layout();
42709     },
42710
42711     onRegionCollapsed : function(region){
42712         this.fireEvent("regioncollapsed", region);
42713     },
42714
42715     onRegionExpanded : function(region){
42716         this.fireEvent("regionexpanded", region);
42717     },
42718
42719     /**
42720      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42721      * performs box-model adjustments.
42722      * @return {Object} The size as an object {width: (the width), height: (the height)}
42723      */
42724     getViewSize : function()
42725     {
42726         var size;
42727         if(this.el.dom != document.body){
42728             size = this.el.getSize();
42729         }else{
42730             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42731         }
42732         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42733         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42734         return size;
42735     },
42736
42737     /**
42738      * Returns the Element this layout is bound to.
42739      * @return {Roo.Element}
42740      */
42741     getEl : function(){
42742         return this.el;
42743     },
42744
42745     /**
42746      * Returns the specified region.
42747      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42748      * @return {Roo.LayoutRegion}
42749      */
42750     getRegion : function(target){
42751         return this.regions[target.toLowerCase()];
42752     },
42753
42754     onWindowResize : function(){
42755         if(this.monitorWindowResize){
42756             this.layout();
42757         }
42758     }
42759 });
42760 /*
42761  * Based on:
42762  * Ext JS Library 1.1.1
42763  * Copyright(c) 2006-2007, Ext JS, LLC.
42764  *
42765  * Originally Released Under LGPL - original licence link has changed is not relivant.
42766  *
42767  * Fork - LGPL
42768  * <script type="text/javascript">
42769  */
42770 /**
42771  * @class Roo.bootstrap.layout.Border
42772  * @extends Roo.bootstrap.layout.Manager
42773  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42774  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42775  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42776  * please see: examples/bootstrap/nested.html<br><br>
42777  
42778 <b>The container the layout is rendered into can be either the body element or any other element.
42779 If it is not the body element, the container needs to either be an absolute positioned element,
42780 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42781 the container size if it is not the body element.</b>
42782
42783 * @constructor
42784 * Create a new Border
42785 * @param {Object} config Configuration options
42786  */
42787 Roo.bootstrap.layout.Border = function(config){
42788     config = config || {};
42789     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42790     
42791     
42792     
42793     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42794         if(config[region]){
42795             config[region].region = region;
42796             this.addRegion(config[region]);
42797         }
42798     },this);
42799     
42800 };
42801
42802 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
42803
42804 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42805     
42806         /**
42807          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42808          */
42809         /**
42810          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42811          */
42812         /**
42813          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42814          */
42815         /**
42816          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42817          */
42818         /**
42819          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42820          */
42821         
42822         
42823         
42824         
42825     parent : false, // this might point to a 'nest' or a ???
42826     
42827     /**
42828      * Creates and adds a new region if it doesn't already exist.
42829      * @param {String} target The target region key (north, south, east, west or center).
42830      * @param {Object} config The regions config object
42831      * @return {BorderLayoutRegion} The new region
42832      */
42833     addRegion : function(config)
42834     {
42835         if(!this.regions[config.region]){
42836             var r = this.factory(config);
42837             this.bindRegion(r);
42838         }
42839         return this.regions[config.region];
42840     },
42841
42842     // private (kinda)
42843     bindRegion : function(r){
42844         this.regions[r.config.region] = r;
42845         
42846         r.on("visibilitychange",    this.layout, this);
42847         r.on("paneladded",          this.layout, this);
42848         r.on("panelremoved",        this.layout, this);
42849         r.on("invalidated",         this.layout, this);
42850         r.on("resized",             this.onRegionResized, this);
42851         r.on("collapsed",           this.onRegionCollapsed, this);
42852         r.on("expanded",            this.onRegionExpanded, this);
42853     },
42854
42855     /**
42856      * Performs a layout update.
42857      */
42858     layout : function()
42859     {
42860         if(this.updating) {
42861             return;
42862         }
42863         
42864         // render all the rebions if they have not been done alreayd?
42865         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42866             if(this.regions[region] && !this.regions[region].bodyEl){
42867                 this.regions[region].onRender(this.el)
42868             }
42869         },this);
42870         
42871         var size = this.getViewSize();
42872         var w = size.width;
42873         var h = size.height;
42874         var centerW = w;
42875         var centerH = h;
42876         var centerY = 0;
42877         var centerX = 0;
42878         //var x = 0, y = 0;
42879
42880         var rs = this.regions;
42881         var north = rs["north"];
42882         var south = rs["south"]; 
42883         var west = rs["west"];
42884         var east = rs["east"];
42885         var center = rs["center"];
42886         //if(this.hideOnLayout){ // not supported anymore
42887             //c.el.setStyle("display", "none");
42888         //}
42889         if(north && north.isVisible()){
42890             var b = north.getBox();
42891             var m = north.getMargins();
42892             b.width = w - (m.left+m.right);
42893             b.x = m.left;
42894             b.y = m.top;
42895             centerY = b.height + b.y + m.bottom;
42896             centerH -= centerY;
42897             north.updateBox(this.safeBox(b));
42898         }
42899         if(south && south.isVisible()){
42900             var b = south.getBox();
42901             var m = south.getMargins();
42902             b.width = w - (m.left+m.right);
42903             b.x = m.left;
42904             var totalHeight = (b.height + m.top + m.bottom);
42905             b.y = h - totalHeight + m.top;
42906             centerH -= totalHeight;
42907             south.updateBox(this.safeBox(b));
42908         }
42909         if(west && west.isVisible()){
42910             var b = west.getBox();
42911             var m = west.getMargins();
42912             b.height = centerH - (m.top+m.bottom);
42913             b.x = m.left;
42914             b.y = centerY + m.top;
42915             var totalWidth = (b.width + m.left + m.right);
42916             centerX += totalWidth;
42917             centerW -= totalWidth;
42918             west.updateBox(this.safeBox(b));
42919         }
42920         if(east && east.isVisible()){
42921             var b = east.getBox();
42922             var m = east.getMargins();
42923             b.height = centerH - (m.top+m.bottom);
42924             var totalWidth = (b.width + m.left + m.right);
42925             b.x = w - totalWidth + m.left;
42926             b.y = centerY + m.top;
42927             centerW -= totalWidth;
42928             east.updateBox(this.safeBox(b));
42929         }
42930         if(center){
42931             var m = center.getMargins();
42932             var centerBox = {
42933                 x: centerX + m.left,
42934                 y: centerY + m.top,
42935                 width: centerW - (m.left+m.right),
42936                 height: centerH - (m.top+m.bottom)
42937             };
42938             //if(this.hideOnLayout){
42939                 //center.el.setStyle("display", "block");
42940             //}
42941             center.updateBox(this.safeBox(centerBox));
42942         }
42943         this.el.repaint();
42944         this.fireEvent("layout", this);
42945     },
42946
42947     // private
42948     safeBox : function(box){
42949         box.width = Math.max(0, box.width);
42950         box.height = Math.max(0, box.height);
42951         return box;
42952     },
42953
42954     /**
42955      * Adds a ContentPanel (or subclass) to this layout.
42956      * @param {String} target The target region key (north, south, east, west or center).
42957      * @param {Roo.ContentPanel} panel The panel to add
42958      * @return {Roo.ContentPanel} The added panel
42959      */
42960     add : function(target, panel){
42961          
42962         target = target.toLowerCase();
42963         return this.regions[target].add(panel);
42964     },
42965
42966     /**
42967      * Remove a ContentPanel (or subclass) to this layout.
42968      * @param {String} target The target region key (north, south, east, west or center).
42969      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42970      * @return {Roo.ContentPanel} The removed panel
42971      */
42972     remove : function(target, panel){
42973         target = target.toLowerCase();
42974         return this.regions[target].remove(panel);
42975     },
42976
42977     /**
42978      * Searches all regions for a panel with the specified id
42979      * @param {String} panelId
42980      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42981      */
42982     findPanel : function(panelId){
42983         var rs = this.regions;
42984         for(var target in rs){
42985             if(typeof rs[target] != "function"){
42986                 var p = rs[target].getPanel(panelId);
42987                 if(p){
42988                     return p;
42989                 }
42990             }
42991         }
42992         return null;
42993     },
42994
42995     /**
42996      * Searches all regions for a panel with the specified id and activates (shows) it.
42997      * @param {String/ContentPanel} panelId The panels id or the panel itself
42998      * @return {Roo.ContentPanel} The shown panel or null
42999      */
43000     showPanel : function(panelId) {
43001       var rs = this.regions;
43002       for(var target in rs){
43003          var r = rs[target];
43004          if(typeof r != "function"){
43005             if(r.hasPanel(panelId)){
43006                return r.showPanel(panelId);
43007             }
43008          }
43009       }
43010       return null;
43011    },
43012
43013    /**
43014      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43015      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43016      */
43017    /*
43018     restoreState : function(provider){
43019         if(!provider){
43020             provider = Roo.state.Manager;
43021         }
43022         var sm = new Roo.LayoutStateManager();
43023         sm.init(this, provider);
43024     },
43025 */
43026  
43027  
43028     /**
43029      * Adds a xtype elements to the layout.
43030      * <pre><code>
43031
43032 layout.addxtype({
43033        xtype : 'ContentPanel',
43034        region: 'west',
43035        items: [ .... ]
43036    }
43037 );
43038
43039 layout.addxtype({
43040         xtype : 'NestedLayoutPanel',
43041         region: 'west',
43042         layout: {
43043            center: { },
43044            west: { }   
43045         },
43046         items : [ ... list of content panels or nested layout panels.. ]
43047    }
43048 );
43049 </code></pre>
43050      * @param {Object} cfg Xtype definition of item to add.
43051      */
43052     addxtype : function(cfg)
43053     {
43054         // basically accepts a pannel...
43055         // can accept a layout region..!?!?
43056         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43057         
43058         
43059         // theory?  children can only be panels??
43060         
43061         //if (!cfg.xtype.match(/Panel$/)) {
43062         //    return false;
43063         //}
43064         var ret = false;
43065         
43066         if (typeof(cfg.region) == 'undefined') {
43067             Roo.log("Failed to add Panel, region was not set");
43068             Roo.log(cfg);
43069             return false;
43070         }
43071         var region = cfg.region;
43072         delete cfg.region;
43073         
43074           
43075         var xitems = [];
43076         if (cfg.items) {
43077             xitems = cfg.items;
43078             delete cfg.items;
43079         }
43080         var nb = false;
43081         
43082         if ( region == 'center') {
43083             Roo.log("Center: " + cfg.title);
43084         }
43085         
43086         
43087         switch(cfg.xtype) 
43088         {
43089             case 'Content':  // ContentPanel (el, cfg)
43090             case 'Scroll':  // ContentPanel (el, cfg)
43091             case 'View': 
43092                 cfg.autoCreate = cfg.autoCreate || true;
43093                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43094                 //} else {
43095                 //    var el = this.el.createChild();
43096                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43097                 //}
43098                 
43099                 this.add(region, ret);
43100                 break;
43101             
43102             /*
43103             case 'TreePanel': // our new panel!
43104                 cfg.el = this.el.createChild();
43105                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43106                 this.add(region, ret);
43107                 break;
43108             */
43109             
43110             case 'Nest': 
43111                 // create a new Layout (which is  a Border Layout...
43112                 
43113                 var clayout = cfg.layout;
43114                 clayout.el  = this.el.createChild();
43115                 clayout.items   = clayout.items  || [];
43116                 
43117                 delete cfg.layout;
43118                 
43119                 // replace this exitems with the clayout ones..
43120                 xitems = clayout.items;
43121                  
43122                 // force background off if it's in center...
43123                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43124                     cfg.background = false;
43125                 }
43126                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
43127                 
43128                 
43129                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43130                 //console.log('adding nested layout panel '  + cfg.toSource());
43131                 this.add(region, ret);
43132                 nb = {}; /// find first...
43133                 break;
43134             
43135             case 'Grid':
43136                 
43137                 // needs grid and region
43138                 
43139                 //var el = this.getRegion(region).el.createChild();
43140                 /*
43141                  *var el = this.el.createChild();
43142                 // create the grid first...
43143                 cfg.grid.container = el;
43144                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43145                 */
43146                 
43147                 if (region == 'center' && this.active ) {
43148                     cfg.background = false;
43149                 }
43150                 
43151                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43152                 
43153                 this.add(region, ret);
43154                 /*
43155                 if (cfg.background) {
43156                     // render grid on panel activation (if panel background)
43157                     ret.on('activate', function(gp) {
43158                         if (!gp.grid.rendered) {
43159                     //        gp.grid.render(el);
43160                         }
43161                     });
43162                 } else {
43163                   //  cfg.grid.render(el);
43164                 }
43165                 */
43166                 break;
43167            
43168            
43169             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43170                 // it was the old xcomponent building that caused this before.
43171                 // espeically if border is the top element in the tree.
43172                 ret = this;
43173                 break; 
43174                 
43175                     
43176                 
43177                 
43178                 
43179             default:
43180                 /*
43181                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43182                     
43183                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43184                     this.add(region, ret);
43185                 } else {
43186                 */
43187                     Roo.log(cfg);
43188                     throw "Can not add '" + cfg.xtype + "' to Border";
43189                     return null;
43190              
43191                                 
43192              
43193         }
43194         this.beginUpdate();
43195         // add children..
43196         var region = '';
43197         var abn = {};
43198         Roo.each(xitems, function(i)  {
43199             region = nb && i.region ? i.region : false;
43200             
43201             var add = ret.addxtype(i);
43202            
43203             if (region) {
43204                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43205                 if (!i.background) {
43206                     abn[region] = nb[region] ;
43207                 }
43208             }
43209             
43210         });
43211         this.endUpdate();
43212
43213         // make the last non-background panel active..
43214         //if (nb) { Roo.log(abn); }
43215         if (nb) {
43216             
43217             for(var r in abn) {
43218                 region = this.getRegion(r);
43219                 if (region) {
43220                     // tried using nb[r], but it does not work..
43221                      
43222                     region.showPanel(abn[r]);
43223                    
43224                 }
43225             }
43226         }
43227         return ret;
43228         
43229     },
43230     
43231     
43232 // private
43233     factory : function(cfg)
43234     {
43235         
43236         var validRegions = Roo.bootstrap.layout.Border.regions;
43237
43238         var target = cfg.region;
43239         cfg.mgr = this;
43240         
43241         var r = Roo.bootstrap.layout;
43242         Roo.log(target);
43243         switch(target){
43244             case "north":
43245                 return new r.North(cfg);
43246             case "south":
43247                 return new r.South(cfg);
43248             case "east":
43249                 return new r.East(cfg);
43250             case "west":
43251                 return new r.West(cfg);
43252             case "center":
43253                 return new r.Center(cfg);
43254         }
43255         throw 'Layout region "'+target+'" not supported.';
43256     }
43257     
43258     
43259 });
43260  /*
43261  * Based on:
43262  * Ext JS Library 1.1.1
43263  * Copyright(c) 2006-2007, Ext JS, LLC.
43264  *
43265  * Originally Released Under LGPL - original licence link has changed is not relivant.
43266  *
43267  * Fork - LGPL
43268  * <script type="text/javascript">
43269  */
43270  
43271 /**
43272  * @class Roo.bootstrap.layout.Basic
43273  * @extends Roo.util.Observable
43274  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43275  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43276  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43277  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43278  * @cfg {string}   region  the region that it inhabits..
43279  * @cfg {bool}   skipConfig skip config?
43280  * 
43281
43282  */
43283 Roo.bootstrap.layout.Basic = function(config){
43284     
43285     this.mgr = config.mgr;
43286     
43287     this.position = config.region;
43288     
43289     var skipConfig = config.skipConfig;
43290     
43291     this.events = {
43292         /**
43293          * @scope Roo.BasicLayoutRegion
43294          */
43295         
43296         /**
43297          * @event beforeremove
43298          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43299          * @param {Roo.LayoutRegion} this
43300          * @param {Roo.ContentPanel} panel The panel
43301          * @param {Object} e The cancel event object
43302          */
43303         "beforeremove" : true,
43304         /**
43305          * @event invalidated
43306          * Fires when the layout for this region is changed.
43307          * @param {Roo.LayoutRegion} this
43308          */
43309         "invalidated" : true,
43310         /**
43311          * @event visibilitychange
43312          * Fires when this region is shown or hidden 
43313          * @param {Roo.LayoutRegion} this
43314          * @param {Boolean} visibility true or false
43315          */
43316         "visibilitychange" : true,
43317         /**
43318          * @event paneladded
43319          * Fires when a panel is added. 
43320          * @param {Roo.LayoutRegion} this
43321          * @param {Roo.ContentPanel} panel The panel
43322          */
43323         "paneladded" : true,
43324         /**
43325          * @event panelremoved
43326          * Fires when a panel is removed. 
43327          * @param {Roo.LayoutRegion} this
43328          * @param {Roo.ContentPanel} panel The panel
43329          */
43330         "panelremoved" : true,
43331         /**
43332          * @event beforecollapse
43333          * Fires when this region before collapse.
43334          * @param {Roo.LayoutRegion} this
43335          */
43336         "beforecollapse" : true,
43337         /**
43338          * @event collapsed
43339          * Fires when this region is collapsed.
43340          * @param {Roo.LayoutRegion} this
43341          */
43342         "collapsed" : true,
43343         /**
43344          * @event expanded
43345          * Fires when this region is expanded.
43346          * @param {Roo.LayoutRegion} this
43347          */
43348         "expanded" : true,
43349         /**
43350          * @event slideshow
43351          * Fires when this region is slid into view.
43352          * @param {Roo.LayoutRegion} this
43353          */
43354         "slideshow" : true,
43355         /**
43356          * @event slidehide
43357          * Fires when this region slides out of view. 
43358          * @param {Roo.LayoutRegion} this
43359          */
43360         "slidehide" : true,
43361         /**
43362          * @event panelactivated
43363          * Fires when a panel is activated. 
43364          * @param {Roo.LayoutRegion} this
43365          * @param {Roo.ContentPanel} panel The activated panel
43366          */
43367         "panelactivated" : true,
43368         /**
43369          * @event resized
43370          * Fires when the user resizes this region. 
43371          * @param {Roo.LayoutRegion} this
43372          * @param {Number} newSize The new size (width for east/west, height for north/south)
43373          */
43374         "resized" : true
43375     };
43376     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43377     this.panels = new Roo.util.MixedCollection();
43378     this.panels.getKey = this.getPanelId.createDelegate(this);
43379     this.box = null;
43380     this.activePanel = null;
43381     // ensure listeners are added...
43382     
43383     if (config.listeners || config.events) {
43384         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43385             listeners : config.listeners || {},
43386             events : config.events || {}
43387         });
43388     }
43389     
43390     if(skipConfig !== true){
43391         this.applyConfig(config);
43392     }
43393 };
43394
43395 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43396 {
43397     getPanelId : function(p){
43398         return p.getId();
43399     },
43400     
43401     applyConfig : function(config){
43402         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43403         this.config = config;
43404         
43405     },
43406     
43407     /**
43408      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43409      * the width, for horizontal (north, south) the height.
43410      * @param {Number} newSize The new width or height
43411      */
43412     resizeTo : function(newSize){
43413         var el = this.el ? this.el :
43414                  (this.activePanel ? this.activePanel.getEl() : null);
43415         if(el){
43416             switch(this.position){
43417                 case "east":
43418                 case "west":
43419                     el.setWidth(newSize);
43420                     this.fireEvent("resized", this, newSize);
43421                 break;
43422                 case "north":
43423                 case "south":
43424                     el.setHeight(newSize);
43425                     this.fireEvent("resized", this, newSize);
43426                 break;                
43427             }
43428         }
43429     },
43430     
43431     getBox : function(){
43432         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43433     },
43434     
43435     getMargins : function(){
43436         return this.margins;
43437     },
43438     
43439     updateBox : function(box){
43440         this.box = box;
43441         var el = this.activePanel.getEl();
43442         el.dom.style.left = box.x + "px";
43443         el.dom.style.top = box.y + "px";
43444         this.activePanel.setSize(box.width, box.height);
43445     },
43446     
43447     /**
43448      * Returns the container element for this region.
43449      * @return {Roo.Element}
43450      */
43451     getEl : function(){
43452         return this.activePanel;
43453     },
43454     
43455     /**
43456      * Returns true if this region is currently visible.
43457      * @return {Boolean}
43458      */
43459     isVisible : function(){
43460         return this.activePanel ? true : false;
43461     },
43462     
43463     setActivePanel : function(panel){
43464         panel = this.getPanel(panel);
43465         if(this.activePanel && this.activePanel != panel){
43466             this.activePanel.setActiveState(false);
43467             this.activePanel.getEl().setLeftTop(-10000,-10000);
43468         }
43469         this.activePanel = panel;
43470         panel.setActiveState(true);
43471         if(this.box){
43472             panel.setSize(this.box.width, this.box.height);
43473         }
43474         this.fireEvent("panelactivated", this, panel);
43475         this.fireEvent("invalidated");
43476     },
43477     
43478     /**
43479      * Show the specified panel.
43480      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43481      * @return {Roo.ContentPanel} The shown panel or null
43482      */
43483     showPanel : function(panel){
43484         panel = this.getPanel(panel);
43485         if(panel){
43486             this.setActivePanel(panel);
43487         }
43488         return panel;
43489     },
43490     
43491     /**
43492      * Get the active panel for this region.
43493      * @return {Roo.ContentPanel} The active panel or null
43494      */
43495     getActivePanel : function(){
43496         return this.activePanel;
43497     },
43498     
43499     /**
43500      * Add the passed ContentPanel(s)
43501      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43502      * @return {Roo.ContentPanel} The panel added (if only one was added)
43503      */
43504     add : function(panel){
43505         if(arguments.length > 1){
43506             for(var i = 0, len = arguments.length; i < len; i++) {
43507                 this.add(arguments[i]);
43508             }
43509             return null;
43510         }
43511         if(this.hasPanel(panel)){
43512             this.showPanel(panel);
43513             return panel;
43514         }
43515         var el = panel.getEl();
43516         if(el.dom.parentNode != this.mgr.el.dom){
43517             this.mgr.el.dom.appendChild(el.dom);
43518         }
43519         if(panel.setRegion){
43520             panel.setRegion(this);
43521         }
43522         this.panels.add(panel);
43523         el.setStyle("position", "absolute");
43524         if(!panel.background){
43525             this.setActivePanel(panel);
43526             if(this.config.initialSize && this.panels.getCount()==1){
43527                 this.resizeTo(this.config.initialSize);
43528             }
43529         }
43530         this.fireEvent("paneladded", this, panel);
43531         return panel;
43532     },
43533     
43534     /**
43535      * Returns true if the panel is in this region.
43536      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43537      * @return {Boolean}
43538      */
43539     hasPanel : function(panel){
43540         if(typeof panel == "object"){ // must be panel obj
43541             panel = panel.getId();
43542         }
43543         return this.getPanel(panel) ? true : false;
43544     },
43545     
43546     /**
43547      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43548      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43549      * @param {Boolean} preservePanel Overrides the config preservePanel option
43550      * @return {Roo.ContentPanel} The panel that was removed
43551      */
43552     remove : function(panel, preservePanel){
43553         panel = this.getPanel(panel);
43554         if(!panel){
43555             return null;
43556         }
43557         var e = {};
43558         this.fireEvent("beforeremove", this, panel, e);
43559         if(e.cancel === true){
43560             return null;
43561         }
43562         var panelId = panel.getId();
43563         this.panels.removeKey(panelId);
43564         return panel;
43565     },
43566     
43567     /**
43568      * Returns the panel specified or null if it's not in this region.
43569      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43570      * @return {Roo.ContentPanel}
43571      */
43572     getPanel : function(id){
43573         if(typeof id == "object"){ // must be panel obj
43574             return id;
43575         }
43576         return this.panels.get(id);
43577     },
43578     
43579     /**
43580      * Returns this regions position (north/south/east/west/center).
43581      * @return {String} 
43582      */
43583     getPosition: function(){
43584         return this.position;    
43585     }
43586 });/*
43587  * Based on:
43588  * Ext JS Library 1.1.1
43589  * Copyright(c) 2006-2007, Ext JS, LLC.
43590  *
43591  * Originally Released Under LGPL - original licence link has changed is not relivant.
43592  *
43593  * Fork - LGPL
43594  * <script type="text/javascript">
43595  */
43596  
43597 /**
43598  * @class Roo.bootstrap.layout.Region
43599  * @extends Roo.bootstrap.layout.Basic
43600  * This class represents a region in a layout manager.
43601  
43602  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43603  * @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})
43604  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43605  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43606  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43607  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43608  * @cfg {String}    title           The title for the region (overrides panel titles)
43609  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43610  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43611  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43612  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43613  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43614  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43615  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43616  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43617  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43618  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43619
43620  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43621  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43622  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43623  * @cfg {Number}    width           For East/West panels
43624  * @cfg {Number}    height          For North/South panels
43625  * @cfg {Boolean}   split           To show the splitter
43626  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43627  * 
43628  * @cfg {string}   cls             Extra CSS classes to add to region
43629  * 
43630  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43631  * @cfg {string}   region  the region that it inhabits..
43632  *
43633
43634  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43635  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43636
43637  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43638  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43639  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43640  */
43641 Roo.bootstrap.layout.Region = function(config)
43642 {
43643     this.applyConfig(config);
43644
43645     var mgr = config.mgr;
43646     var pos = config.region;
43647     config.skipConfig = true;
43648     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43649     
43650     if (mgr.el) {
43651         this.onRender(mgr.el);   
43652     }
43653      
43654     this.visible = true;
43655     this.collapsed = false;
43656     this.unrendered_panels = [];
43657 };
43658
43659 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43660
43661     position: '', // set by wrapper (eg. north/south etc..)
43662     unrendered_panels : null,  // unrendered panels.
43663     
43664     tabPosition : false,
43665     
43666     mgr: false, // points to 'Border'
43667     
43668     
43669     createBody : function(){
43670         /** This region's body element 
43671         * @type Roo.Element */
43672         this.bodyEl = this.el.createChild({
43673                 tag: "div",
43674                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43675         });
43676     },
43677
43678     onRender: function(ctr, pos)
43679     {
43680         var dh = Roo.DomHelper;
43681         /** This region's container element 
43682         * @type Roo.Element */
43683         this.el = dh.append(ctr.dom, {
43684                 tag: "div",
43685                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43686             }, true);
43687         /** This region's title element 
43688         * @type Roo.Element */
43689     
43690         this.titleEl = dh.append(this.el.dom,  {
43691                 tag: "div",
43692                 unselectable: "on",
43693                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43694                 children:[
43695                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43696                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43697                 ]
43698             }, true);
43699         
43700         this.titleEl.enableDisplayMode();
43701         /** This region's title text element 
43702         * @type HTMLElement */
43703         this.titleTextEl = this.titleEl.dom.firstChild;
43704         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43705         /*
43706         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43707         this.closeBtn.enableDisplayMode();
43708         this.closeBtn.on("click", this.closeClicked, this);
43709         this.closeBtn.hide();
43710     */
43711         this.createBody(this.config);
43712         if(this.config.hideWhenEmpty){
43713             this.hide();
43714             this.on("paneladded", this.validateVisibility, this);
43715             this.on("panelremoved", this.validateVisibility, this);
43716         }
43717         if(this.autoScroll){
43718             this.bodyEl.setStyle("overflow", "auto");
43719         }else{
43720             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43721         }
43722         //if(c.titlebar !== false){
43723             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43724                 this.titleEl.hide();
43725             }else{
43726                 this.titleEl.show();
43727                 if(this.config.title){
43728                     this.titleTextEl.innerHTML = this.config.title;
43729                 }
43730             }
43731         //}
43732         if(this.config.collapsed){
43733             this.collapse(true);
43734         }
43735         if(this.config.hidden){
43736             this.hide();
43737         }
43738         
43739         if (this.unrendered_panels && this.unrendered_panels.length) {
43740             for (var i =0;i< this.unrendered_panels.length; i++) {
43741                 this.add(this.unrendered_panels[i]);
43742             }
43743             this.unrendered_panels = null;
43744             
43745         }
43746         
43747     },
43748     
43749     applyConfig : function(c)
43750     {
43751         /*
43752          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43753             var dh = Roo.DomHelper;
43754             if(c.titlebar !== false){
43755                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43756                 this.collapseBtn.on("click", this.collapse, this);
43757                 this.collapseBtn.enableDisplayMode();
43758                 /*
43759                 if(c.showPin === true || this.showPin){
43760                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43761                     this.stickBtn.enableDisplayMode();
43762                     this.stickBtn.on("click", this.expand, this);
43763                     this.stickBtn.hide();
43764                 }
43765                 
43766             }
43767             */
43768             /** This region's collapsed element
43769             * @type Roo.Element */
43770             /*
43771              *
43772             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43773                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43774             ]}, true);
43775             
43776             if(c.floatable !== false){
43777                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43778                this.collapsedEl.on("click", this.collapseClick, this);
43779             }
43780
43781             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43782                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43783                    id: "message", unselectable: "on", style:{"float":"left"}});
43784                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43785              }
43786             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43787             this.expandBtn.on("click", this.expand, this);
43788             
43789         }
43790         
43791         if(this.collapseBtn){
43792             this.collapseBtn.setVisible(c.collapsible == true);
43793         }
43794         
43795         this.cmargins = c.cmargins || this.cmargins ||
43796                          (this.position == "west" || this.position == "east" ?
43797                              {top: 0, left: 2, right:2, bottom: 0} :
43798                              {top: 2, left: 0, right:0, bottom: 2});
43799         */
43800         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43801         
43802         
43803         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43804         
43805         this.autoScroll = c.autoScroll || false;
43806         
43807         
43808        
43809         
43810         this.duration = c.duration || .30;
43811         this.slideDuration = c.slideDuration || .45;
43812         this.config = c;
43813        
43814     },
43815     /**
43816      * Returns true if this region is currently visible.
43817      * @return {Boolean}
43818      */
43819     isVisible : function(){
43820         return this.visible;
43821     },
43822
43823     /**
43824      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43825      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43826      */
43827     //setCollapsedTitle : function(title){
43828     //    title = title || "&#160;";
43829      //   if(this.collapsedTitleTextEl){
43830       //      this.collapsedTitleTextEl.innerHTML = title;
43831        // }
43832     //},
43833
43834     getBox : function(){
43835         var b;
43836       //  if(!this.collapsed){
43837             b = this.el.getBox(false, true);
43838        // }else{
43839           //  b = this.collapsedEl.getBox(false, true);
43840         //}
43841         return b;
43842     },
43843
43844     getMargins : function(){
43845         return this.margins;
43846         //return this.collapsed ? this.cmargins : this.margins;
43847     },
43848 /*
43849     highlight : function(){
43850         this.el.addClass("x-layout-panel-dragover");
43851     },
43852
43853     unhighlight : function(){
43854         this.el.removeClass("x-layout-panel-dragover");
43855     },
43856 */
43857     updateBox : function(box)
43858     {
43859         if (!this.bodyEl) {
43860             return; // not rendered yet..
43861         }
43862         
43863         this.box = box;
43864         if(!this.collapsed){
43865             this.el.dom.style.left = box.x + "px";
43866             this.el.dom.style.top = box.y + "px";
43867             this.updateBody(box.width, box.height);
43868         }else{
43869             this.collapsedEl.dom.style.left = box.x + "px";
43870             this.collapsedEl.dom.style.top = box.y + "px";
43871             this.collapsedEl.setSize(box.width, box.height);
43872         }
43873         if(this.tabs){
43874             this.tabs.autoSizeTabs();
43875         }
43876     },
43877
43878     updateBody : function(w, h)
43879     {
43880         if(w !== null){
43881             this.el.setWidth(w);
43882             w -= this.el.getBorderWidth("rl");
43883             if(this.config.adjustments){
43884                 w += this.config.adjustments[0];
43885             }
43886         }
43887         if(h !== null && h > 0){
43888             this.el.setHeight(h);
43889             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43890             h -= this.el.getBorderWidth("tb");
43891             if(this.config.adjustments){
43892                 h += this.config.adjustments[1];
43893             }
43894             this.bodyEl.setHeight(h);
43895             if(this.tabs){
43896                 h = this.tabs.syncHeight(h);
43897             }
43898         }
43899         if(this.panelSize){
43900             w = w !== null ? w : this.panelSize.width;
43901             h = h !== null ? h : this.panelSize.height;
43902         }
43903         if(this.activePanel){
43904             var el = this.activePanel.getEl();
43905             w = w !== null ? w : el.getWidth();
43906             h = h !== null ? h : el.getHeight();
43907             this.panelSize = {width: w, height: h};
43908             this.activePanel.setSize(w, h);
43909         }
43910         if(Roo.isIE && this.tabs){
43911             this.tabs.el.repaint();
43912         }
43913     },
43914
43915     /**
43916      * Returns the container element for this region.
43917      * @return {Roo.Element}
43918      */
43919     getEl : function(){
43920         return this.el;
43921     },
43922
43923     /**
43924      * Hides this region.
43925      */
43926     hide : function(){
43927         //if(!this.collapsed){
43928             this.el.dom.style.left = "-2000px";
43929             this.el.hide();
43930         //}else{
43931          //   this.collapsedEl.dom.style.left = "-2000px";
43932          //   this.collapsedEl.hide();
43933        // }
43934         this.visible = false;
43935         this.fireEvent("visibilitychange", this, false);
43936     },
43937
43938     /**
43939      * Shows this region if it was previously hidden.
43940      */
43941     show : function(){
43942         //if(!this.collapsed){
43943             this.el.show();
43944         //}else{
43945         //    this.collapsedEl.show();
43946        // }
43947         this.visible = true;
43948         this.fireEvent("visibilitychange", this, true);
43949     },
43950 /*
43951     closeClicked : function(){
43952         if(this.activePanel){
43953             this.remove(this.activePanel);
43954         }
43955     },
43956
43957     collapseClick : function(e){
43958         if(this.isSlid){
43959            e.stopPropagation();
43960            this.slideIn();
43961         }else{
43962            e.stopPropagation();
43963            this.slideOut();
43964         }
43965     },
43966 */
43967     /**
43968      * Collapses this region.
43969      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43970      */
43971     /*
43972     collapse : function(skipAnim, skipCheck = false){
43973         if(this.collapsed) {
43974             return;
43975         }
43976         
43977         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43978             
43979             this.collapsed = true;
43980             if(this.split){
43981                 this.split.el.hide();
43982             }
43983             if(this.config.animate && skipAnim !== true){
43984                 this.fireEvent("invalidated", this);
43985                 this.animateCollapse();
43986             }else{
43987                 this.el.setLocation(-20000,-20000);
43988                 this.el.hide();
43989                 this.collapsedEl.show();
43990                 this.fireEvent("collapsed", this);
43991                 this.fireEvent("invalidated", this);
43992             }
43993         }
43994         
43995     },
43996 */
43997     animateCollapse : function(){
43998         // overridden
43999     },
44000
44001     /**
44002      * Expands this region if it was previously collapsed.
44003      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44004      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44005      */
44006     /*
44007     expand : function(e, skipAnim){
44008         if(e) {
44009             e.stopPropagation();
44010         }
44011         if(!this.collapsed || this.el.hasActiveFx()) {
44012             return;
44013         }
44014         if(this.isSlid){
44015             this.afterSlideIn();
44016             skipAnim = true;
44017         }
44018         this.collapsed = false;
44019         if(this.config.animate && skipAnim !== true){
44020             this.animateExpand();
44021         }else{
44022             this.el.show();
44023             if(this.split){
44024                 this.split.el.show();
44025             }
44026             this.collapsedEl.setLocation(-2000,-2000);
44027             this.collapsedEl.hide();
44028             this.fireEvent("invalidated", this);
44029             this.fireEvent("expanded", this);
44030         }
44031     },
44032 */
44033     animateExpand : function(){
44034         // overridden
44035     },
44036
44037     initTabs : function()
44038     {
44039         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
44040         
44041         var ts = new Roo.bootstrap.panel.Tabs({
44042             el: this.bodyEl.dom,
44043             region : this,
44044             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
44045             disableTooltips: this.config.disableTabTips,
44046             toolbar : this.config.toolbar
44047         });
44048         
44049         if(this.config.hideTabs){
44050             ts.stripWrap.setDisplayed(false);
44051         }
44052         this.tabs = ts;
44053         ts.resizeTabs = this.config.resizeTabs === true;
44054         ts.minTabWidth = this.config.minTabWidth || 40;
44055         ts.maxTabWidth = this.config.maxTabWidth || 250;
44056         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44057         ts.monitorResize = false;
44058         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44059         ts.bodyEl.addClass('roo-layout-tabs-body');
44060         this.panels.each(this.initPanelAsTab, this);
44061     },
44062
44063     initPanelAsTab : function(panel){
44064         var ti = this.tabs.addTab(
44065             panel.getEl().id,
44066             panel.getTitle(),
44067             null,
44068             this.config.closeOnTab && panel.isClosable(),
44069             panel.tpl
44070         );
44071         if(panel.tabTip !== undefined){
44072             ti.setTooltip(panel.tabTip);
44073         }
44074         ti.on("activate", function(){
44075               this.setActivePanel(panel);
44076         }, this);
44077         
44078         if(this.config.closeOnTab){
44079             ti.on("beforeclose", function(t, e){
44080                 e.cancel = true;
44081                 this.remove(panel);
44082             }, this);
44083         }
44084         
44085         panel.tabItem = ti;
44086         
44087         return ti;
44088     },
44089
44090     updatePanelTitle : function(panel, title)
44091     {
44092         if(this.activePanel == panel){
44093             this.updateTitle(title);
44094         }
44095         if(this.tabs){
44096             var ti = this.tabs.getTab(panel.getEl().id);
44097             ti.setText(title);
44098             if(panel.tabTip !== undefined){
44099                 ti.setTooltip(panel.tabTip);
44100             }
44101         }
44102     },
44103
44104     updateTitle : function(title){
44105         if(this.titleTextEl && !this.config.title){
44106             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44107         }
44108     },
44109
44110     setActivePanel : function(panel)
44111     {
44112         panel = this.getPanel(panel);
44113         if(this.activePanel && this.activePanel != panel){
44114             if(this.activePanel.setActiveState(false) === false){
44115                 return;
44116             }
44117         }
44118         this.activePanel = panel;
44119         panel.setActiveState(true);
44120         if(this.panelSize){
44121             panel.setSize(this.panelSize.width, this.panelSize.height);
44122         }
44123         if(this.closeBtn){
44124             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44125         }
44126         this.updateTitle(panel.getTitle());
44127         if(this.tabs){
44128             this.fireEvent("invalidated", this);
44129         }
44130         this.fireEvent("panelactivated", this, panel);
44131     },
44132
44133     /**
44134      * Shows the specified panel.
44135      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44136      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44137      */
44138     showPanel : function(panel)
44139     {
44140         panel = this.getPanel(panel);
44141         if(panel){
44142             if(this.tabs){
44143                 var tab = this.tabs.getTab(panel.getEl().id);
44144                 if(tab.isHidden()){
44145                     this.tabs.unhideTab(tab.id);
44146                 }
44147                 tab.activate();
44148             }else{
44149                 this.setActivePanel(panel);
44150             }
44151         }
44152         return panel;
44153     },
44154
44155     /**
44156      * Get the active panel for this region.
44157      * @return {Roo.ContentPanel} The active panel or null
44158      */
44159     getActivePanel : function(){
44160         return this.activePanel;
44161     },
44162
44163     validateVisibility : function(){
44164         if(this.panels.getCount() < 1){
44165             this.updateTitle("&#160;");
44166             this.closeBtn.hide();
44167             this.hide();
44168         }else{
44169             if(!this.isVisible()){
44170                 this.show();
44171             }
44172         }
44173     },
44174
44175     /**
44176      * Adds the passed ContentPanel(s) to this region.
44177      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44178      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44179      */
44180     add : function(panel)
44181     {
44182         if(arguments.length > 1){
44183             for(var i = 0, len = arguments.length; i < len; i++) {
44184                 this.add(arguments[i]);
44185             }
44186             return null;
44187         }
44188         
44189         // if we have not been rendered yet, then we can not really do much of this..
44190         if (!this.bodyEl) {
44191             this.unrendered_panels.push(panel);
44192             return panel;
44193         }
44194         
44195         
44196         
44197         
44198         if(this.hasPanel(panel)){
44199             this.showPanel(panel);
44200             return panel;
44201         }
44202         panel.setRegion(this);
44203         this.panels.add(panel);
44204        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44205             // sinle panel - no tab...?? would it not be better to render it with the tabs,
44206             // and hide them... ???
44207             this.bodyEl.dom.appendChild(panel.getEl().dom);
44208             if(panel.background !== true){
44209                 this.setActivePanel(panel);
44210             }
44211             this.fireEvent("paneladded", this, panel);
44212             return panel;
44213         }
44214         */
44215         if(!this.tabs){
44216             this.initTabs();
44217         }else{
44218             this.initPanelAsTab(panel);
44219         }
44220         
44221         
44222         if(panel.background !== true){
44223             this.tabs.activate(panel.getEl().id);
44224         }
44225         this.fireEvent("paneladded", this, panel);
44226         return panel;
44227     },
44228
44229     /**
44230      * Hides the tab for the specified panel.
44231      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44232      */
44233     hidePanel : function(panel){
44234         if(this.tabs && (panel = this.getPanel(panel))){
44235             this.tabs.hideTab(panel.getEl().id);
44236         }
44237     },
44238
44239     /**
44240      * Unhides the tab for a previously hidden panel.
44241      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44242      */
44243     unhidePanel : function(panel){
44244         if(this.tabs && (panel = this.getPanel(panel))){
44245             this.tabs.unhideTab(panel.getEl().id);
44246         }
44247     },
44248
44249     clearPanels : function(){
44250         while(this.panels.getCount() > 0){
44251              this.remove(this.panels.first());
44252         }
44253     },
44254
44255     /**
44256      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44257      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44258      * @param {Boolean} preservePanel Overrides the config preservePanel option
44259      * @return {Roo.ContentPanel} The panel that was removed
44260      */
44261     remove : function(panel, preservePanel)
44262     {
44263         panel = this.getPanel(panel);
44264         if(!panel){
44265             return null;
44266         }
44267         var e = {};
44268         this.fireEvent("beforeremove", this, panel, e);
44269         if(e.cancel === true){
44270             return null;
44271         }
44272         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44273         var panelId = panel.getId();
44274         this.panels.removeKey(panelId);
44275         if(preservePanel){
44276             document.body.appendChild(panel.getEl().dom);
44277         }
44278         if(this.tabs){
44279             this.tabs.removeTab(panel.getEl().id);
44280         }else if (!preservePanel){
44281             this.bodyEl.dom.removeChild(panel.getEl().dom);
44282         }
44283         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44284             var p = this.panels.first();
44285             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44286             tempEl.appendChild(p.getEl().dom);
44287             this.bodyEl.update("");
44288             this.bodyEl.dom.appendChild(p.getEl().dom);
44289             tempEl = null;
44290             this.updateTitle(p.getTitle());
44291             this.tabs = null;
44292             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44293             this.setActivePanel(p);
44294         }
44295         panel.setRegion(null);
44296         if(this.activePanel == panel){
44297             this.activePanel = null;
44298         }
44299         if(this.config.autoDestroy !== false && preservePanel !== true){
44300             try{panel.destroy();}catch(e){}
44301         }
44302         this.fireEvent("panelremoved", this, panel);
44303         return panel;
44304     },
44305
44306     /**
44307      * Returns the TabPanel component used by this region
44308      * @return {Roo.TabPanel}
44309      */
44310     getTabs : function(){
44311         return this.tabs;
44312     },
44313
44314     createTool : function(parentEl, className){
44315         var btn = Roo.DomHelper.append(parentEl, {
44316             tag: "div",
44317             cls: "x-layout-tools-button",
44318             children: [ {
44319                 tag: "div",
44320                 cls: "roo-layout-tools-button-inner " + className,
44321                 html: "&#160;"
44322             }]
44323         }, true);
44324         btn.addClassOnOver("roo-layout-tools-button-over");
44325         return btn;
44326     }
44327 });/*
44328  * Based on:
44329  * Ext JS Library 1.1.1
44330  * Copyright(c) 2006-2007, Ext JS, LLC.
44331  *
44332  * Originally Released Under LGPL - original licence link has changed is not relivant.
44333  *
44334  * Fork - LGPL
44335  * <script type="text/javascript">
44336  */
44337  
44338
44339
44340 /**
44341  * @class Roo.SplitLayoutRegion
44342  * @extends Roo.LayoutRegion
44343  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44344  */
44345 Roo.bootstrap.layout.Split = function(config){
44346     this.cursor = config.cursor;
44347     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44348 };
44349
44350 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44351 {
44352     splitTip : "Drag to resize.",
44353     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44354     useSplitTips : false,
44355
44356     applyConfig : function(config){
44357         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44358     },
44359     
44360     onRender : function(ctr,pos) {
44361         
44362         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44363         if(!this.config.split){
44364             return;
44365         }
44366         if(!this.split){
44367             
44368             var splitEl = Roo.DomHelper.append(ctr.dom,  {
44369                             tag: "div",
44370                             id: this.el.id + "-split",
44371                             cls: "roo-layout-split roo-layout-split-"+this.position,
44372                             html: "&#160;"
44373             });
44374             /** The SplitBar for this region 
44375             * @type Roo.SplitBar */
44376             // does not exist yet...
44377             Roo.log([this.position, this.orientation]);
44378             
44379             this.split = new Roo.bootstrap.SplitBar({
44380                 dragElement : splitEl,
44381                 resizingElement: this.el,
44382                 orientation : this.orientation
44383             });
44384             
44385             this.split.on("moved", this.onSplitMove, this);
44386             this.split.useShim = this.config.useShim === true;
44387             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44388             if(this.useSplitTips){
44389                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44390             }
44391             //if(config.collapsible){
44392             //    this.split.el.on("dblclick", this.collapse,  this);
44393             //}
44394         }
44395         if(typeof this.config.minSize != "undefined"){
44396             this.split.minSize = this.config.minSize;
44397         }
44398         if(typeof this.config.maxSize != "undefined"){
44399             this.split.maxSize = this.config.maxSize;
44400         }
44401         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44402             this.hideSplitter();
44403         }
44404         
44405     },
44406
44407     getHMaxSize : function(){
44408          var cmax = this.config.maxSize || 10000;
44409          var center = this.mgr.getRegion("center");
44410          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44411     },
44412
44413     getVMaxSize : function(){
44414          var cmax = this.config.maxSize || 10000;
44415          var center = this.mgr.getRegion("center");
44416          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44417     },
44418
44419     onSplitMove : function(split, newSize){
44420         this.fireEvent("resized", this, newSize);
44421     },
44422     
44423     /** 
44424      * Returns the {@link Roo.SplitBar} for this region.
44425      * @return {Roo.SplitBar}
44426      */
44427     getSplitBar : function(){
44428         return this.split;
44429     },
44430     
44431     hide : function(){
44432         this.hideSplitter();
44433         Roo.bootstrap.layout.Split.superclass.hide.call(this);
44434     },
44435
44436     hideSplitter : function(){
44437         if(this.split){
44438             this.split.el.setLocation(-2000,-2000);
44439             this.split.el.hide();
44440         }
44441     },
44442
44443     show : function(){
44444         if(this.split){
44445             this.split.el.show();
44446         }
44447         Roo.bootstrap.layout.Split.superclass.show.call(this);
44448     },
44449     
44450     beforeSlide: function(){
44451         if(Roo.isGecko){// firefox overflow auto bug workaround
44452             this.bodyEl.clip();
44453             if(this.tabs) {
44454                 this.tabs.bodyEl.clip();
44455             }
44456             if(this.activePanel){
44457                 this.activePanel.getEl().clip();
44458                 
44459                 if(this.activePanel.beforeSlide){
44460                     this.activePanel.beforeSlide();
44461                 }
44462             }
44463         }
44464     },
44465     
44466     afterSlide : function(){
44467         if(Roo.isGecko){// firefox overflow auto bug workaround
44468             this.bodyEl.unclip();
44469             if(this.tabs) {
44470                 this.tabs.bodyEl.unclip();
44471             }
44472             if(this.activePanel){
44473                 this.activePanel.getEl().unclip();
44474                 if(this.activePanel.afterSlide){
44475                     this.activePanel.afterSlide();
44476                 }
44477             }
44478         }
44479     },
44480
44481     initAutoHide : function(){
44482         if(this.autoHide !== false){
44483             if(!this.autoHideHd){
44484                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44485                 this.autoHideHd = {
44486                     "mouseout": function(e){
44487                         if(!e.within(this.el, true)){
44488                             st.delay(500);
44489                         }
44490                     },
44491                     "mouseover" : function(e){
44492                         st.cancel();
44493                     },
44494                     scope : this
44495                 };
44496             }
44497             this.el.on(this.autoHideHd);
44498         }
44499     },
44500
44501     clearAutoHide : function(){
44502         if(this.autoHide !== false){
44503             this.el.un("mouseout", this.autoHideHd.mouseout);
44504             this.el.un("mouseover", this.autoHideHd.mouseover);
44505         }
44506     },
44507
44508     clearMonitor : function(){
44509         Roo.get(document).un("click", this.slideInIf, this);
44510     },
44511
44512     // these names are backwards but not changed for compat
44513     slideOut : function(){
44514         if(this.isSlid || this.el.hasActiveFx()){
44515             return;
44516         }
44517         this.isSlid = true;
44518         if(this.collapseBtn){
44519             this.collapseBtn.hide();
44520         }
44521         this.closeBtnState = this.closeBtn.getStyle('display');
44522         this.closeBtn.hide();
44523         if(this.stickBtn){
44524             this.stickBtn.show();
44525         }
44526         this.el.show();
44527         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44528         this.beforeSlide();
44529         this.el.setStyle("z-index", 10001);
44530         this.el.slideIn(this.getSlideAnchor(), {
44531             callback: function(){
44532                 this.afterSlide();
44533                 this.initAutoHide();
44534                 Roo.get(document).on("click", this.slideInIf, this);
44535                 this.fireEvent("slideshow", this);
44536             },
44537             scope: this,
44538             block: true
44539         });
44540     },
44541
44542     afterSlideIn : function(){
44543         this.clearAutoHide();
44544         this.isSlid = false;
44545         this.clearMonitor();
44546         this.el.setStyle("z-index", "");
44547         if(this.collapseBtn){
44548             this.collapseBtn.show();
44549         }
44550         this.closeBtn.setStyle('display', this.closeBtnState);
44551         if(this.stickBtn){
44552             this.stickBtn.hide();
44553         }
44554         this.fireEvent("slidehide", this);
44555     },
44556
44557     slideIn : function(cb){
44558         if(!this.isSlid || this.el.hasActiveFx()){
44559             Roo.callback(cb);
44560             return;
44561         }
44562         this.isSlid = false;
44563         this.beforeSlide();
44564         this.el.slideOut(this.getSlideAnchor(), {
44565             callback: function(){
44566                 this.el.setLeftTop(-10000, -10000);
44567                 this.afterSlide();
44568                 this.afterSlideIn();
44569                 Roo.callback(cb);
44570             },
44571             scope: this,
44572             block: true
44573         });
44574     },
44575     
44576     slideInIf : function(e){
44577         if(!e.within(this.el)){
44578             this.slideIn();
44579         }
44580     },
44581
44582     animateCollapse : function(){
44583         this.beforeSlide();
44584         this.el.setStyle("z-index", 20000);
44585         var anchor = this.getSlideAnchor();
44586         this.el.slideOut(anchor, {
44587             callback : function(){
44588                 this.el.setStyle("z-index", "");
44589                 this.collapsedEl.slideIn(anchor, {duration:.3});
44590                 this.afterSlide();
44591                 this.el.setLocation(-10000,-10000);
44592                 this.el.hide();
44593                 this.fireEvent("collapsed", this);
44594             },
44595             scope: this,
44596             block: true
44597         });
44598     },
44599
44600     animateExpand : function(){
44601         this.beforeSlide();
44602         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44603         this.el.setStyle("z-index", 20000);
44604         this.collapsedEl.hide({
44605             duration:.1
44606         });
44607         this.el.slideIn(this.getSlideAnchor(), {
44608             callback : function(){
44609                 this.el.setStyle("z-index", "");
44610                 this.afterSlide();
44611                 if(this.split){
44612                     this.split.el.show();
44613                 }
44614                 this.fireEvent("invalidated", this);
44615                 this.fireEvent("expanded", this);
44616             },
44617             scope: this,
44618             block: true
44619         });
44620     },
44621
44622     anchors : {
44623         "west" : "left",
44624         "east" : "right",
44625         "north" : "top",
44626         "south" : "bottom"
44627     },
44628
44629     sanchors : {
44630         "west" : "l",
44631         "east" : "r",
44632         "north" : "t",
44633         "south" : "b"
44634     },
44635
44636     canchors : {
44637         "west" : "tl-tr",
44638         "east" : "tr-tl",
44639         "north" : "tl-bl",
44640         "south" : "bl-tl"
44641     },
44642
44643     getAnchor : function(){
44644         return this.anchors[this.position];
44645     },
44646
44647     getCollapseAnchor : function(){
44648         return this.canchors[this.position];
44649     },
44650
44651     getSlideAnchor : function(){
44652         return this.sanchors[this.position];
44653     },
44654
44655     getAlignAdj : function(){
44656         var cm = this.cmargins;
44657         switch(this.position){
44658             case "west":
44659                 return [0, 0];
44660             break;
44661             case "east":
44662                 return [0, 0];
44663             break;
44664             case "north":
44665                 return [0, 0];
44666             break;
44667             case "south":
44668                 return [0, 0];
44669             break;
44670         }
44671     },
44672
44673     getExpandAdj : function(){
44674         var c = this.collapsedEl, cm = this.cmargins;
44675         switch(this.position){
44676             case "west":
44677                 return [-(cm.right+c.getWidth()+cm.left), 0];
44678             break;
44679             case "east":
44680                 return [cm.right+c.getWidth()+cm.left, 0];
44681             break;
44682             case "north":
44683                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44684             break;
44685             case "south":
44686                 return [0, cm.top+cm.bottom+c.getHeight()];
44687             break;
44688         }
44689     }
44690 });/*
44691  * Based on:
44692  * Ext JS Library 1.1.1
44693  * Copyright(c) 2006-2007, Ext JS, LLC.
44694  *
44695  * Originally Released Under LGPL - original licence link has changed is not relivant.
44696  *
44697  * Fork - LGPL
44698  * <script type="text/javascript">
44699  */
44700 /*
44701  * These classes are private internal classes
44702  */
44703 Roo.bootstrap.layout.Center = function(config){
44704     config.region = "center";
44705     Roo.bootstrap.layout.Region.call(this, config);
44706     this.visible = true;
44707     this.minWidth = config.minWidth || 20;
44708     this.minHeight = config.minHeight || 20;
44709 };
44710
44711 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44712     hide : function(){
44713         // center panel can't be hidden
44714     },
44715     
44716     show : function(){
44717         // center panel can't be hidden
44718     },
44719     
44720     getMinWidth: function(){
44721         return this.minWidth;
44722     },
44723     
44724     getMinHeight: function(){
44725         return this.minHeight;
44726     }
44727 });
44728
44729
44730
44731
44732  
44733
44734
44735
44736
44737
44738
44739 Roo.bootstrap.layout.North = function(config)
44740 {
44741     config.region = 'north';
44742     config.cursor = 'n-resize';
44743     
44744     Roo.bootstrap.layout.Split.call(this, config);
44745     
44746     
44747     if(this.split){
44748         this.split.placement = Roo.bootstrap.SplitBar.TOP;
44749         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44750         this.split.el.addClass("roo-layout-split-v");
44751     }
44752     //var size = config.initialSize || config.height;
44753     //if(this.el && typeof size != "undefined"){
44754     //    this.el.setHeight(size);
44755     //}
44756 };
44757 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44758 {
44759     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44760      
44761      
44762     onRender : function(ctr, pos)
44763     {
44764         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44765         var size = this.config.initialSize || this.config.height;
44766         if(this.el && typeof size != "undefined"){
44767             this.el.setHeight(size);
44768         }
44769     
44770     },
44771     
44772     getBox : function(){
44773         if(this.collapsed){
44774             return this.collapsedEl.getBox();
44775         }
44776         var box = this.el.getBox();
44777         if(this.split){
44778             box.height += this.split.el.getHeight();
44779         }
44780         return box;
44781     },
44782     
44783     updateBox : function(box){
44784         if(this.split && !this.collapsed){
44785             box.height -= this.split.el.getHeight();
44786             this.split.el.setLeft(box.x);
44787             this.split.el.setTop(box.y+box.height);
44788             this.split.el.setWidth(box.width);
44789         }
44790         if(this.collapsed){
44791             this.updateBody(box.width, null);
44792         }
44793         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44794     }
44795 });
44796
44797
44798
44799
44800
44801 Roo.bootstrap.layout.South = function(config){
44802     config.region = 'south';
44803     config.cursor = 's-resize';
44804     Roo.bootstrap.layout.Split.call(this, config);
44805     if(this.split){
44806         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44807         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44808         this.split.el.addClass("roo-layout-split-v");
44809     }
44810     
44811 };
44812
44813 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44814     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44815     
44816     onRender : function(ctr, pos)
44817     {
44818         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44819         var size = this.config.initialSize || this.config.height;
44820         if(this.el && typeof size != "undefined"){
44821             this.el.setHeight(size);
44822         }
44823     
44824     },
44825     
44826     getBox : function(){
44827         if(this.collapsed){
44828             return this.collapsedEl.getBox();
44829         }
44830         var box = this.el.getBox();
44831         if(this.split){
44832             var sh = this.split.el.getHeight();
44833             box.height += sh;
44834             box.y -= sh;
44835         }
44836         return box;
44837     },
44838     
44839     updateBox : function(box){
44840         if(this.split && !this.collapsed){
44841             var sh = this.split.el.getHeight();
44842             box.height -= sh;
44843             box.y += sh;
44844             this.split.el.setLeft(box.x);
44845             this.split.el.setTop(box.y-sh);
44846             this.split.el.setWidth(box.width);
44847         }
44848         if(this.collapsed){
44849             this.updateBody(box.width, null);
44850         }
44851         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44852     }
44853 });
44854
44855 Roo.bootstrap.layout.East = function(config){
44856     config.region = "east";
44857     config.cursor = "e-resize";
44858     Roo.bootstrap.layout.Split.call(this, config);
44859     if(this.split){
44860         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44861         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44862         this.split.el.addClass("roo-layout-split-h");
44863     }
44864     
44865 };
44866 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44867     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44868     
44869     onRender : function(ctr, pos)
44870     {
44871         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44872         var size = this.config.initialSize || this.config.width;
44873         if(this.el && typeof size != "undefined"){
44874             this.el.setWidth(size);
44875         }
44876     
44877     },
44878     
44879     getBox : function(){
44880         if(this.collapsed){
44881             return this.collapsedEl.getBox();
44882         }
44883         var box = this.el.getBox();
44884         if(this.split){
44885             var sw = this.split.el.getWidth();
44886             box.width += sw;
44887             box.x -= sw;
44888         }
44889         return box;
44890     },
44891
44892     updateBox : function(box){
44893         if(this.split && !this.collapsed){
44894             var sw = this.split.el.getWidth();
44895             box.width -= sw;
44896             this.split.el.setLeft(box.x);
44897             this.split.el.setTop(box.y);
44898             this.split.el.setHeight(box.height);
44899             box.x += sw;
44900         }
44901         if(this.collapsed){
44902             this.updateBody(null, box.height);
44903         }
44904         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44905     }
44906 });
44907
44908 Roo.bootstrap.layout.West = function(config){
44909     config.region = "west";
44910     config.cursor = "w-resize";
44911     
44912     Roo.bootstrap.layout.Split.call(this, config);
44913     if(this.split){
44914         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44915         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44916         this.split.el.addClass("roo-layout-split-h");
44917     }
44918     
44919 };
44920 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44921     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44922     
44923     onRender: function(ctr, pos)
44924     {
44925         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44926         var size = this.config.initialSize || this.config.width;
44927         if(typeof size != "undefined"){
44928             this.el.setWidth(size);
44929         }
44930     },
44931     
44932     getBox : function(){
44933         if(this.collapsed){
44934             return this.collapsedEl.getBox();
44935         }
44936         var box = this.el.getBox();
44937         if (box.width == 0) {
44938             box.width = this.config.width; // kludge?
44939         }
44940         if(this.split){
44941             box.width += this.split.el.getWidth();
44942         }
44943         return box;
44944     },
44945     
44946     updateBox : function(box){
44947         if(this.split && !this.collapsed){
44948             var sw = this.split.el.getWidth();
44949             box.width -= sw;
44950             this.split.el.setLeft(box.x+box.width);
44951             this.split.el.setTop(box.y);
44952             this.split.el.setHeight(box.height);
44953         }
44954         if(this.collapsed){
44955             this.updateBody(null, box.height);
44956         }
44957         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44958     }
44959 });/*
44960  * Based on:
44961  * Ext JS Library 1.1.1
44962  * Copyright(c) 2006-2007, Ext JS, LLC.
44963  *
44964  * Originally Released Under LGPL - original licence link has changed is not relivant.
44965  *
44966  * Fork - LGPL
44967  * <script type="text/javascript">
44968  */
44969 /**
44970  * @class Roo.bootstrap.paenl.Content
44971  * @extends Roo.util.Observable
44972  * @children Roo.bootstrap.Component
44973  * @parent builder Roo.bootstrap.layout.Border
44974  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44975  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44976  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44977  * @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
44978  * @cfg {Boolean}   closable      True if the panel can be closed/removed
44979  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
44980  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44981  * @cfg {Toolbar}   toolbar       A toolbar for this panel
44982  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
44983  * @cfg {String} title          The title for this panel
44984  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44985  * @cfg {String} url            Calls {@link #setUrl} with this value
44986  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44987  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
44988  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
44989  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
44990  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
44991  * @cfg {Boolean} badges render the badges
44992  * @cfg {String} cls  extra classes to use  
44993  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44994  
44995  * @constructor
44996  * Create a new ContentPanel.
44997  * @param {String/Object} config A string to set only the title or a config object
44998  
44999  */
45000 Roo.bootstrap.panel.Content = function( config){
45001     
45002     this.tpl = config.tpl || false;
45003     
45004     var el = config.el;
45005     var content = config.content;
45006
45007     if(config.autoCreate){ // xtype is available if this is called from factory
45008         el = Roo.id();
45009     }
45010     this.el = Roo.get(el);
45011     if(!this.el && config && config.autoCreate){
45012         if(typeof config.autoCreate == "object"){
45013             if(!config.autoCreate.id){
45014                 config.autoCreate.id = config.id||el;
45015             }
45016             this.el = Roo.DomHelper.append(document.body,
45017                         config.autoCreate, true);
45018         }else{
45019             var elcfg =  {
45020                 tag: "div",
45021                 cls: (config.cls || '') +
45022                     (config.background ? ' bg-' + config.background : '') +
45023                     " roo-layout-inactive-content",
45024                 id: config.id||el
45025             };
45026             if (config.iframe) {
45027                 elcfg.cn = [
45028                     {
45029                         tag : 'iframe',
45030                         style : 'border: 0px',
45031                         src : 'about:blank'
45032                     }
45033                 ];
45034             }
45035               
45036             if (config.html) {
45037                 elcfg.html = config.html;
45038                 
45039             }
45040                         
45041             this.el = Roo.DomHelper.append(document.body, elcfg , true);
45042             if (config.iframe) {
45043                 this.iframeEl = this.el.select('iframe',true).first();
45044             }
45045             
45046         }
45047     } 
45048     this.closable = false;
45049     this.loaded = false;
45050     this.active = false;
45051    
45052       
45053     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
45054         
45055         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45056         
45057         this.wrapEl = this.el; //this.el.wrap();
45058         var ti = [];
45059         if (config.toolbar.items) {
45060             ti = config.toolbar.items ;
45061             delete config.toolbar.items ;
45062         }
45063         
45064         var nitems = [];
45065         this.toolbar.render(this.wrapEl, 'before');
45066         for(var i =0;i < ti.length;i++) {
45067           //  Roo.log(['add child', items[i]]);
45068             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45069         }
45070         this.toolbar.items = nitems;
45071         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45072         delete config.toolbar;
45073         
45074     }
45075     /*
45076     // xtype created footer. - not sure if will work as we normally have to render first..
45077     if (this.footer && !this.footer.el && this.footer.xtype) {
45078         if (!this.wrapEl) {
45079             this.wrapEl = this.el.wrap();
45080         }
45081     
45082         this.footer.container = this.wrapEl.createChild();
45083          
45084         this.footer = Roo.factory(this.footer, Roo);
45085         
45086     }
45087     */
45088     
45089      if(typeof config == "string"){
45090         this.title = config;
45091     }else{
45092         Roo.apply(this, config);
45093     }
45094     
45095     if(this.resizeEl){
45096         this.resizeEl = Roo.get(this.resizeEl, true);
45097     }else{
45098         this.resizeEl = this.el;
45099     }
45100     // handle view.xtype
45101     
45102  
45103     
45104     
45105     this.addEvents({
45106         /**
45107          * @event activate
45108          * Fires when this panel is activated. 
45109          * @param {Roo.ContentPanel} this
45110          */
45111         "activate" : true,
45112         /**
45113          * @event deactivate
45114          * Fires when this panel is activated. 
45115          * @param {Roo.ContentPanel} this
45116          */
45117         "deactivate" : true,
45118
45119         /**
45120          * @event resize
45121          * Fires when this panel is resized if fitToFrame is true.
45122          * @param {Roo.ContentPanel} this
45123          * @param {Number} width The width after any component adjustments
45124          * @param {Number} height The height after any component adjustments
45125          */
45126         "resize" : true,
45127         
45128          /**
45129          * @event render
45130          * Fires when this tab is created
45131          * @param {Roo.ContentPanel} this
45132          */
45133         "render" : true,
45134         
45135           /**
45136          * @event scroll
45137          * Fires when this content is scrolled
45138          * @param {Roo.ContentPanel} this
45139          * @param {Event} scrollEvent
45140          */
45141         "scroll" : true
45142         
45143         
45144         
45145     });
45146     
45147
45148     
45149     
45150     if(this.autoScroll && !this.iframe){
45151         this.resizeEl.setStyle("overflow", "auto");
45152         this.resizeEl.on('scroll', this.onScroll, this);
45153     } else {
45154         // fix randome scrolling
45155         //this.el.on('scroll', function() {
45156         //    Roo.log('fix random scolling');
45157         //    this.scrollTo('top',0); 
45158         //});
45159     }
45160     content = content || this.content;
45161     if(content){
45162         this.setContent(content);
45163     }
45164     if(config && config.url){
45165         this.setUrl(this.url, this.params, this.loadOnce);
45166     }
45167     
45168     
45169     
45170     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45171     
45172     if (this.view && typeof(this.view.xtype) != 'undefined') {
45173         this.view.el = this.el.appendChild(document.createElement("div"));
45174         this.view = Roo.factory(this.view); 
45175         this.view.render  &&  this.view.render(false, '');  
45176     }
45177     
45178     
45179     this.fireEvent('render', this);
45180 };
45181
45182 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45183     
45184     cls : '',
45185     background : '',
45186     
45187     tabTip : '',
45188     
45189     iframe : false,
45190     iframeEl : false,
45191     
45192     /* Resize Element - use this to work out scroll etc. */
45193     resizeEl : false,
45194     
45195     setRegion : function(region){
45196         this.region = region;
45197         this.setActiveClass(region && !this.background);
45198     },
45199     
45200     
45201     setActiveClass: function(state)
45202     {
45203         if(state){
45204            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45205            this.el.setStyle('position','relative');
45206         }else{
45207            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45208            this.el.setStyle('position', 'absolute');
45209         } 
45210     },
45211     
45212     /**
45213      * Returns the toolbar for this Panel if one was configured. 
45214      * @return {Roo.Toolbar} 
45215      */
45216     getToolbar : function(){
45217         return this.toolbar;
45218     },
45219     
45220     setActiveState : function(active)
45221     {
45222         this.active = active;
45223         this.setActiveClass(active);
45224         if(!active){
45225             if(this.fireEvent("deactivate", this) === false){
45226                 return false;
45227             }
45228             return true;
45229         }
45230         this.fireEvent("activate", this);
45231         return true;
45232     },
45233     /**
45234      * Updates this panel's element (not for iframe)
45235      * @param {String} content The new content
45236      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45237     */
45238     setContent : function(content, loadScripts){
45239         if (this.iframe) {
45240             return;
45241         }
45242         
45243         this.el.update(content, loadScripts);
45244     },
45245
45246     ignoreResize : function(w, h)
45247     {
45248         //return false; // always resize?
45249         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45250             return true;
45251         }else{
45252             this.lastSize = {width: w, height: h};
45253             return false;
45254         }
45255     },
45256     /**
45257      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45258      * @return {Roo.UpdateManager} The UpdateManager
45259      */
45260     getUpdateManager : function(){
45261         if (this.iframe) {
45262             return false;
45263         }
45264         return this.el.getUpdateManager();
45265     },
45266      /**
45267      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45268      * Does not work with IFRAME contents
45269      * @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:
45270 <pre><code>
45271 panel.load({
45272     url: "your-url.php",
45273     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45274     callback: yourFunction,
45275     scope: yourObject, //(optional scope)
45276     discardUrl: false,
45277     nocache: false,
45278     text: "Loading...",
45279     timeout: 30,
45280     scripts: false
45281 });
45282 </code></pre>
45283      
45284      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45285      * 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.
45286      * @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}
45287      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45288      * @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.
45289      * @return {Roo.ContentPanel} this
45290      */
45291     load : function(){
45292         
45293         if (this.iframe) {
45294             return this;
45295         }
45296         
45297         var um = this.el.getUpdateManager();
45298         um.update.apply(um, arguments);
45299         return this;
45300     },
45301
45302
45303     /**
45304      * 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.
45305      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45306      * @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)
45307      * @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)
45308      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45309      */
45310     setUrl : function(url, params, loadOnce){
45311         if (this.iframe) {
45312             this.iframeEl.dom.src = url;
45313             return false;
45314         }
45315         
45316         if(this.refreshDelegate){
45317             this.removeListener("activate", this.refreshDelegate);
45318         }
45319         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45320         this.on("activate", this.refreshDelegate);
45321         return this.el.getUpdateManager();
45322     },
45323     
45324     _handleRefresh : function(url, params, loadOnce){
45325         if(!loadOnce || !this.loaded){
45326             var updater = this.el.getUpdateManager();
45327             updater.update(url, params, this._setLoaded.createDelegate(this));
45328         }
45329     },
45330     
45331     _setLoaded : function(){
45332         this.loaded = true;
45333     }, 
45334     
45335     /**
45336      * Returns this panel's id
45337      * @return {String} 
45338      */
45339     getId : function(){
45340         return this.el.id;
45341     },
45342     
45343     /** 
45344      * Returns this panel's element - used by regiosn to add.
45345      * @return {Roo.Element} 
45346      */
45347     getEl : function(){
45348         return this.wrapEl || this.el;
45349     },
45350     
45351    
45352     
45353     adjustForComponents : function(width, height)
45354     {
45355         //Roo.log('adjustForComponents ');
45356         if(this.resizeEl != this.el){
45357             width -= this.el.getFrameWidth('lr');
45358             height -= this.el.getFrameWidth('tb');
45359         }
45360         if(this.toolbar){
45361             var te = this.toolbar.getEl();
45362             te.setWidth(width);
45363             height -= te.getHeight();
45364         }
45365         if(this.footer){
45366             var te = this.footer.getEl();
45367             te.setWidth(width);
45368             height -= te.getHeight();
45369         }
45370         
45371         
45372         if(this.adjustments){
45373             width += this.adjustments[0];
45374             height += this.adjustments[1];
45375         }
45376         return {"width": width, "height": height};
45377     },
45378     
45379     setSize : function(width, height){
45380         if(this.fitToFrame && !this.ignoreResize(width, height)){
45381             if(this.fitContainer && this.resizeEl != this.el){
45382                 this.el.setSize(width, height);
45383             }
45384             var size = this.adjustForComponents(width, height);
45385             if (this.iframe) {
45386                 this.iframeEl.setSize(width,height);
45387             }
45388             
45389             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45390             this.fireEvent('resize', this, size.width, size.height);
45391             
45392             
45393         }
45394     },
45395     
45396     /**
45397      * Returns this panel's title
45398      * @return {String} 
45399      */
45400     getTitle : function(){
45401         
45402         if (typeof(this.title) != 'object') {
45403             return this.title;
45404         }
45405         
45406         var t = '';
45407         for (var k in this.title) {
45408             if (!this.title.hasOwnProperty(k)) {
45409                 continue;
45410             }
45411             
45412             if (k.indexOf('-') >= 0) {
45413                 var s = k.split('-');
45414                 for (var i = 0; i<s.length; i++) {
45415                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45416                 }
45417             } else {
45418                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45419             }
45420         }
45421         return t;
45422     },
45423     
45424     /**
45425      * Set this panel's title
45426      * @param {String} title
45427      */
45428     setTitle : function(title){
45429         this.title = title;
45430         if(this.region){
45431             this.region.updatePanelTitle(this, title);
45432         }
45433     },
45434     
45435     /**
45436      * Returns true is this panel was configured to be closable
45437      * @return {Boolean} 
45438      */
45439     isClosable : function(){
45440         return this.closable;
45441     },
45442     
45443     beforeSlide : function(){
45444         this.el.clip();
45445         this.resizeEl.clip();
45446     },
45447     
45448     afterSlide : function(){
45449         this.el.unclip();
45450         this.resizeEl.unclip();
45451     },
45452     
45453     /**
45454      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45455      *   Will fail silently if the {@link #setUrl} method has not been called.
45456      *   This does not activate the panel, just updates its content.
45457      */
45458     refresh : function(){
45459         if(this.refreshDelegate){
45460            this.loaded = false;
45461            this.refreshDelegate();
45462         }
45463     },
45464     
45465     /**
45466      * Destroys this panel
45467      */
45468     destroy : function(){
45469         this.el.removeAllListeners();
45470         var tempEl = document.createElement("span");
45471         tempEl.appendChild(this.el.dom);
45472         tempEl.innerHTML = "";
45473         this.el.remove();
45474         this.el = null;
45475     },
45476     
45477     /**
45478      * form - if the content panel contains a form - this is a reference to it.
45479      * @type {Roo.form.Form}
45480      */
45481     form : false,
45482     /**
45483      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45484      *    This contains a reference to it.
45485      * @type {Roo.View}
45486      */
45487     view : false,
45488     
45489       /**
45490      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45491      * <pre><code>
45492
45493 layout.addxtype({
45494        xtype : 'Form',
45495        items: [ .... ]
45496    }
45497 );
45498
45499 </code></pre>
45500      * @param {Object} cfg Xtype definition of item to add.
45501      */
45502     
45503     
45504     getChildContainer: function () {
45505         return this.getEl();
45506     },
45507     
45508     
45509     onScroll : function(e)
45510     {
45511         this.fireEvent('scroll', this, e);
45512     }
45513     
45514     
45515     /*
45516         var  ret = new Roo.factory(cfg);
45517         return ret;
45518         
45519         
45520         // add form..
45521         if (cfg.xtype.match(/^Form$/)) {
45522             
45523             var el;
45524             //if (this.footer) {
45525             //    el = this.footer.container.insertSibling(false, 'before');
45526             //} else {
45527                 el = this.el.createChild();
45528             //}
45529
45530             this.form = new  Roo.form.Form(cfg);
45531             
45532             
45533             if ( this.form.allItems.length) {
45534                 this.form.render(el.dom);
45535             }
45536             return this.form;
45537         }
45538         // should only have one of theses..
45539         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45540             // views.. should not be just added - used named prop 'view''
45541             
45542             cfg.el = this.el.appendChild(document.createElement("div"));
45543             // factory?
45544             
45545             var ret = new Roo.factory(cfg);
45546              
45547              ret.render && ret.render(false, ''); // render blank..
45548             this.view = ret;
45549             return ret;
45550         }
45551         return false;
45552     }
45553     \*/
45554 });
45555  
45556 /**
45557  * @class Roo.bootstrap.panel.Grid
45558  * @extends Roo.bootstrap.panel.Content
45559  * @constructor
45560  * Create a new GridPanel.
45561  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45562  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45563  * @param {Object} config A the config object
45564   
45565  */
45566
45567
45568
45569 Roo.bootstrap.panel.Grid = function(config)
45570 {
45571     
45572       
45573     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45574         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45575
45576     config.el = this.wrapper;
45577     //this.el = this.wrapper;
45578     
45579       if (config.container) {
45580         // ctor'ed from a Border/panel.grid
45581         
45582         
45583         this.wrapper.setStyle("overflow", "hidden");
45584         this.wrapper.addClass('roo-grid-container');
45585
45586     }
45587     
45588     
45589     if(config.toolbar){
45590         var tool_el = this.wrapper.createChild();    
45591         this.toolbar = Roo.factory(config.toolbar);
45592         var ti = [];
45593         if (config.toolbar.items) {
45594             ti = config.toolbar.items ;
45595             delete config.toolbar.items ;
45596         }
45597         
45598         var nitems = [];
45599         this.toolbar.render(tool_el);
45600         for(var i =0;i < ti.length;i++) {
45601           //  Roo.log(['add child', items[i]]);
45602             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45603         }
45604         this.toolbar.items = nitems;
45605         
45606         delete config.toolbar;
45607     }
45608     
45609     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45610     config.grid.scrollBody = true;;
45611     config.grid.monitorWindowResize = false; // turn off autosizing
45612     config.grid.autoHeight = false;
45613     config.grid.autoWidth = false;
45614     
45615     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45616     
45617     if (config.background) {
45618         // render grid on panel activation (if panel background)
45619         this.on('activate', function(gp) {
45620             if (!gp.grid.rendered) {
45621                 gp.grid.render(this.wrapper);
45622                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45623             }
45624         });
45625             
45626     } else {
45627         this.grid.render(this.wrapper);
45628         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45629
45630     }
45631     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45632     // ??? needed ??? config.el = this.wrapper;
45633     
45634     
45635     
45636   
45637     // xtype created footer. - not sure if will work as we normally have to render first..
45638     if (this.footer && !this.footer.el && this.footer.xtype) {
45639         
45640         var ctr = this.grid.getView().getFooterPanel(true);
45641         this.footer.dataSource = this.grid.dataSource;
45642         this.footer = Roo.factory(this.footer, Roo);
45643         this.footer.render(ctr);
45644         
45645     }
45646     
45647     
45648     
45649     
45650      
45651 };
45652
45653 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45654 {
45655   
45656     getId : function(){
45657         return this.grid.id;
45658     },
45659     
45660     /**
45661      * Returns the grid for this panel
45662      * @return {Roo.bootstrap.Table} 
45663      */
45664     getGrid : function(){
45665         return this.grid;    
45666     },
45667     
45668     setSize : function(width, height)
45669     {
45670      
45671         //if(!this.ignoreResize(width, height)){
45672             var grid = this.grid;
45673             var size = this.adjustForComponents(width, height);
45674             // tfoot is not a footer?
45675           
45676             
45677             var gridel = grid.getGridEl();
45678             gridel.setSize(size.width, size.height);
45679             
45680             var tbd = grid.getGridEl().select('tbody', true).first();
45681             var thd = grid.getGridEl().select('thead',true).first();
45682             var tbf= grid.getGridEl().select('tfoot', true).first();
45683
45684             if (tbf) {
45685                 size.height -= tbf.getHeight();
45686             }
45687             if (thd) {
45688                 size.height -= thd.getHeight();
45689             }
45690             
45691             tbd.setSize(size.width, size.height );
45692             // this is for the account management tab -seems to work there.
45693             var thd = grid.getGridEl().select('thead',true).first();
45694             //if (tbd) {
45695             //    tbd.setSize(size.width, size.height - thd.getHeight());
45696             //}
45697              
45698             grid.autoSize();
45699         //}
45700    
45701     },
45702      
45703     
45704     
45705     beforeSlide : function(){
45706         this.grid.getView().scroller.clip();
45707     },
45708     
45709     afterSlide : function(){
45710         this.grid.getView().scroller.unclip();
45711     },
45712     
45713     destroy : function(){
45714         this.grid.destroy();
45715         delete this.grid;
45716         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
45717     }
45718 });
45719
45720 /**
45721  * @class Roo.bootstrap.panel.Nest
45722  * @extends Roo.bootstrap.panel.Content
45723  * @constructor
45724  * Create a new Panel, that can contain a layout.Border.
45725  * 
45726  * 
45727  * @param {String/Object} config A string to set only the title or a config object
45728  */
45729 Roo.bootstrap.panel.Nest = function(config)
45730 {
45731     // construct with only one argument..
45732     /* FIXME - implement nicer consturctors
45733     if (layout.layout) {
45734         config = layout;
45735         layout = config.layout;
45736         delete config.layout;
45737     }
45738     if (layout.xtype && !layout.getEl) {
45739         // then layout needs constructing..
45740         layout = Roo.factory(layout, Roo);
45741     }
45742     */
45743     
45744     config.el =  config.layout.getEl();
45745     
45746     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45747     
45748     config.layout.monitorWindowResize = false; // turn off autosizing
45749     this.layout = config.layout;
45750     this.layout.getEl().addClass("roo-layout-nested-layout");
45751     this.layout.parent = this;
45752     
45753     
45754     
45755     
45756 };
45757
45758 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45759     /**
45760     * @cfg {Roo.BorderLayout} layout The layout for this panel
45761     */
45762     layout : false,
45763
45764     setSize : function(width, height){
45765         if(!this.ignoreResize(width, height)){
45766             var size = this.adjustForComponents(width, height);
45767             var el = this.layout.getEl();
45768             if (size.height < 1) {
45769                 el.setWidth(size.width);   
45770             } else {
45771                 el.setSize(size.width, size.height);
45772             }
45773             var touch = el.dom.offsetWidth;
45774             this.layout.layout();
45775             // ie requires a double layout on the first pass
45776             if(Roo.isIE && !this.initialized){
45777                 this.initialized = true;
45778                 this.layout.layout();
45779             }
45780         }
45781     },
45782     
45783     // activate all subpanels if not currently active..
45784     
45785     setActiveState : function(active){
45786         this.active = active;
45787         this.setActiveClass(active);
45788         
45789         if(!active){
45790             this.fireEvent("deactivate", this);
45791             return;
45792         }
45793         
45794         this.fireEvent("activate", this);
45795         // not sure if this should happen before or after..
45796         if (!this.layout) {
45797             return; // should not happen..
45798         }
45799         var reg = false;
45800         for (var r in this.layout.regions) {
45801             reg = this.layout.getRegion(r);
45802             if (reg.getActivePanel()) {
45803                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45804                 reg.setActivePanel(reg.getActivePanel());
45805                 continue;
45806             }
45807             if (!reg.panels.length) {
45808                 continue;
45809             }
45810             reg.showPanel(reg.getPanel(0));
45811         }
45812         
45813         
45814         
45815         
45816     },
45817     
45818     /**
45819      * Returns the nested BorderLayout for this panel
45820      * @return {Roo.BorderLayout} 
45821      */
45822     getLayout : function(){
45823         return this.layout;
45824     },
45825     
45826      /**
45827      * Adds a xtype elements to the layout of the nested panel
45828      * <pre><code>
45829
45830 panel.addxtype({
45831        xtype : 'ContentPanel',
45832        region: 'west',
45833        items: [ .... ]
45834    }
45835 );
45836
45837 panel.addxtype({
45838         xtype : 'NestedLayoutPanel',
45839         region: 'west',
45840         layout: {
45841            center: { },
45842            west: { }   
45843         },
45844         items : [ ... list of content panels or nested layout panels.. ]
45845    }
45846 );
45847 </code></pre>
45848      * @param {Object} cfg Xtype definition of item to add.
45849      */
45850     addxtype : function(cfg) {
45851         return this.layout.addxtype(cfg);
45852     
45853     }
45854 });/*
45855  * Based on:
45856  * Ext JS Library 1.1.1
45857  * Copyright(c) 2006-2007, Ext JS, LLC.
45858  *
45859  * Originally Released Under LGPL - original licence link has changed is not relivant.
45860  *
45861  * Fork - LGPL
45862  * <script type="text/javascript">
45863  */
45864 /**
45865  * @class Roo.TabPanel
45866  * @extends Roo.util.Observable
45867  * A lightweight tab container.
45868  * <br><br>
45869  * Usage:
45870  * <pre><code>
45871 // basic tabs 1, built from existing content
45872 var tabs = new Roo.TabPanel("tabs1");
45873 tabs.addTab("script", "View Script");
45874 tabs.addTab("markup", "View Markup");
45875 tabs.activate("script");
45876
45877 // more advanced tabs, built from javascript
45878 var jtabs = new Roo.TabPanel("jtabs");
45879 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45880
45881 // set up the UpdateManager
45882 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45883 var updater = tab2.getUpdateManager();
45884 updater.setDefaultUrl("ajax1.htm");
45885 tab2.on('activate', updater.refresh, updater, true);
45886
45887 // Use setUrl for Ajax loading
45888 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45889 tab3.setUrl("ajax2.htm", null, true);
45890
45891 // Disabled tab
45892 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45893 tab4.disable();
45894
45895 jtabs.activate("jtabs-1");
45896  * </code></pre>
45897  * @constructor
45898  * Create a new TabPanel.
45899  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45900  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45901  */
45902 Roo.bootstrap.panel.Tabs = function(config){
45903     /**
45904     * The container element for this TabPanel.
45905     * @type Roo.Element
45906     */
45907     this.el = Roo.get(config.el);
45908     delete config.el;
45909     if(config){
45910         if(typeof config == "boolean"){
45911             this.tabPosition = config ? "bottom" : "top";
45912         }else{
45913             Roo.apply(this, config);
45914         }
45915     }
45916     
45917     if(this.tabPosition == "bottom"){
45918         // if tabs are at the bottom = create the body first.
45919         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45920         this.el.addClass("roo-tabs-bottom");
45921     }
45922     // next create the tabs holders
45923     
45924     if (this.tabPosition == "west"){
45925         
45926         var reg = this.region; // fake it..
45927         while (reg) {
45928             if (!reg.mgr.parent) {
45929                 break;
45930             }
45931             reg = reg.mgr.parent.region;
45932         }
45933         Roo.log("got nest?");
45934         Roo.log(reg);
45935         if (reg.mgr.getRegion('west')) {
45936             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45937             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45938             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45939             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45940             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45941         
45942             
45943         }
45944         
45945         
45946     } else {
45947      
45948         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45949         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45950         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45951         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45952     }
45953     
45954     
45955     if(Roo.isIE){
45956         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45957     }
45958     
45959     // finally - if tabs are at the top, then create the body last..
45960     if(this.tabPosition != "bottom"){
45961         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45962          * @type Roo.Element
45963          */
45964         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45965         this.el.addClass("roo-tabs-top");
45966     }
45967     this.items = [];
45968
45969     this.bodyEl.setStyle("position", "relative");
45970
45971     this.active = null;
45972     this.activateDelegate = this.activate.createDelegate(this);
45973
45974     this.addEvents({
45975         /**
45976          * @event tabchange
45977          * Fires when the active tab changes
45978          * @param {Roo.TabPanel} this
45979          * @param {Roo.TabPanelItem} activePanel The new active tab
45980          */
45981         "tabchange": true,
45982         /**
45983          * @event beforetabchange
45984          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45985          * @param {Roo.TabPanel} this
45986          * @param {Object} e Set cancel to true on this object to cancel the tab change
45987          * @param {Roo.TabPanelItem} tab The tab being changed to
45988          */
45989         "beforetabchange" : true
45990     });
45991
45992     Roo.EventManager.onWindowResize(this.onResize, this);
45993     this.cpad = this.el.getPadding("lr");
45994     this.hiddenCount = 0;
45995
45996
45997     // toolbar on the tabbar support...
45998     if (this.toolbar) {
45999         alert("no toolbar support yet");
46000         this.toolbar  = false;
46001         /*
46002         var tcfg = this.toolbar;
46003         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
46004         this.toolbar = new Roo.Toolbar(tcfg);
46005         if (Roo.isSafari) {
46006             var tbl = tcfg.container.child('table', true);
46007             tbl.setAttribute('width', '100%');
46008         }
46009         */
46010         
46011     }
46012    
46013
46014
46015     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
46016 };
46017
46018 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
46019     /*
46020      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
46021      */
46022     tabPosition : "top",
46023     /*
46024      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
46025      */
46026     currentTabWidth : 0,
46027     /*
46028      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
46029      */
46030     minTabWidth : 40,
46031     /*
46032      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
46033      */
46034     maxTabWidth : 250,
46035     /*
46036      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
46037      */
46038     preferredTabWidth : 175,
46039     /*
46040      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
46041      */
46042     resizeTabs : false,
46043     /*
46044      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
46045      */
46046     monitorResize : true,
46047     /*
46048      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
46049      */
46050     toolbar : false,  // set by caller..
46051     
46052     region : false, /// set by caller
46053     
46054     disableTooltips : true, // not used yet...
46055
46056     /**
46057      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46058      * @param {String} id The id of the div to use <b>or create</b>
46059      * @param {String} text The text for the tab
46060      * @param {String} content (optional) Content to put in the TabPanelItem body
46061      * @param {Boolean} closable (optional) True to create a close icon on the tab
46062      * @return {Roo.TabPanelItem} The created TabPanelItem
46063      */
46064     addTab : function(id, text, content, closable, tpl)
46065     {
46066         var item = new Roo.bootstrap.panel.TabItem({
46067             panel: this,
46068             id : id,
46069             text : text,
46070             closable : closable,
46071             tpl : tpl
46072         });
46073         this.addTabItem(item);
46074         if(content){
46075             item.setContent(content);
46076         }
46077         return item;
46078     },
46079
46080     /**
46081      * Returns the {@link Roo.TabPanelItem} with the specified id/index
46082      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46083      * @return {Roo.TabPanelItem}
46084      */
46085     getTab : function(id){
46086         return this.items[id];
46087     },
46088
46089     /**
46090      * Hides the {@link Roo.TabPanelItem} with the specified id/index
46091      * @param {String/Number} id The id or index of the TabPanelItem to hide.
46092      */
46093     hideTab : function(id){
46094         var t = this.items[id];
46095         if(!t.isHidden()){
46096            t.setHidden(true);
46097            this.hiddenCount++;
46098            this.autoSizeTabs();
46099         }
46100     },
46101
46102     /**
46103      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46104      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46105      */
46106     unhideTab : function(id){
46107         var t = this.items[id];
46108         if(t.isHidden()){
46109            t.setHidden(false);
46110            this.hiddenCount--;
46111            this.autoSizeTabs();
46112         }
46113     },
46114
46115     /**
46116      * Adds an existing {@link Roo.TabPanelItem}.
46117      * @param {Roo.TabPanelItem} item The TabPanelItem to add
46118      */
46119     addTabItem : function(item)
46120     {
46121         this.items[item.id] = item;
46122         this.items.push(item);
46123         this.autoSizeTabs();
46124       //  if(this.resizeTabs){
46125     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46126   //         this.autoSizeTabs();
46127 //        }else{
46128 //            item.autoSize();
46129        // }
46130     },
46131
46132     /**
46133      * Removes a {@link Roo.TabPanelItem}.
46134      * @param {String/Number} id The id or index of the TabPanelItem to remove.
46135      */
46136     removeTab : function(id){
46137         var items = this.items;
46138         var tab = items[id];
46139         if(!tab) { return; }
46140         var index = items.indexOf(tab);
46141         if(this.active == tab && items.length > 1){
46142             var newTab = this.getNextAvailable(index);
46143             if(newTab) {
46144                 newTab.activate();
46145             }
46146         }
46147         this.stripEl.dom.removeChild(tab.pnode.dom);
46148         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46149             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46150         }
46151         items.splice(index, 1);
46152         delete this.items[tab.id];
46153         tab.fireEvent("close", tab);
46154         tab.purgeListeners();
46155         this.autoSizeTabs();
46156     },
46157
46158     getNextAvailable : function(start){
46159         var items = this.items;
46160         var index = start;
46161         // look for a next tab that will slide over to
46162         // replace the one being removed
46163         while(index < items.length){
46164             var item = items[++index];
46165             if(item && !item.isHidden()){
46166                 return item;
46167             }
46168         }
46169         // if one isn't found select the previous tab (on the left)
46170         index = start;
46171         while(index >= 0){
46172             var item = items[--index];
46173             if(item && !item.isHidden()){
46174                 return item;
46175             }
46176         }
46177         return null;
46178     },
46179
46180     /**
46181      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46182      * @param {String/Number} id The id or index of the TabPanelItem to disable.
46183      */
46184     disableTab : function(id){
46185         var tab = this.items[id];
46186         if(tab && this.active != tab){
46187             tab.disable();
46188         }
46189     },
46190
46191     /**
46192      * Enables a {@link Roo.TabPanelItem} that is disabled.
46193      * @param {String/Number} id The id or index of the TabPanelItem to enable.
46194      */
46195     enableTab : function(id){
46196         var tab = this.items[id];
46197         tab.enable();
46198     },
46199
46200     /**
46201      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46202      * @param {String/Number} id The id or index of the TabPanelItem to activate.
46203      * @return {Roo.TabPanelItem} The TabPanelItem.
46204      */
46205     activate : function(id)
46206     {
46207         //Roo.log('activite:'  + id);
46208         
46209         var tab = this.items[id];
46210         if(!tab){
46211             return null;
46212         }
46213         if(tab == this.active || tab.disabled){
46214             return tab;
46215         }
46216         var e = {};
46217         this.fireEvent("beforetabchange", this, e, tab);
46218         if(e.cancel !== true && !tab.disabled){
46219             if(this.active){
46220                 this.active.hide();
46221             }
46222             this.active = this.items[id];
46223             this.active.show();
46224             this.fireEvent("tabchange", this, this.active);
46225         }
46226         return tab;
46227     },
46228
46229     /**
46230      * Gets the active {@link Roo.TabPanelItem}.
46231      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46232      */
46233     getActiveTab : function(){
46234         return this.active;
46235     },
46236
46237     /**
46238      * Updates the tab body element to fit the height of the container element
46239      * for overflow scrolling
46240      * @param {Number} targetHeight (optional) Override the starting height from the elements height
46241      */
46242     syncHeight : function(targetHeight){
46243         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46244         var bm = this.bodyEl.getMargins();
46245         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46246         this.bodyEl.setHeight(newHeight);
46247         return newHeight;
46248     },
46249
46250     onResize : function(){
46251         if(this.monitorResize){
46252             this.autoSizeTabs();
46253         }
46254     },
46255
46256     /**
46257      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46258      */
46259     beginUpdate : function(){
46260         this.updating = true;
46261     },
46262
46263     /**
46264      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46265      */
46266     endUpdate : function(){
46267         this.updating = false;
46268         this.autoSizeTabs();
46269     },
46270
46271     /**
46272      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46273      */
46274     autoSizeTabs : function()
46275     {
46276         var count = this.items.length;
46277         var vcount = count - this.hiddenCount;
46278         
46279         if (vcount < 2) {
46280             this.stripEl.hide();
46281         } else {
46282             this.stripEl.show();
46283         }
46284         
46285         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46286             return;
46287         }
46288         
46289         
46290         var w = Math.max(this.el.getWidth() - this.cpad, 10);
46291         var availWidth = Math.floor(w / vcount);
46292         var b = this.stripBody;
46293         if(b.getWidth() > w){
46294             var tabs = this.items;
46295             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46296             if(availWidth < this.minTabWidth){
46297                 /*if(!this.sleft){    // incomplete scrolling code
46298                     this.createScrollButtons();
46299                 }
46300                 this.showScroll();
46301                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46302             }
46303         }else{
46304             if(this.currentTabWidth < this.preferredTabWidth){
46305                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46306             }
46307         }
46308     },
46309
46310     /**
46311      * Returns the number of tabs in this TabPanel.
46312      * @return {Number}
46313      */
46314      getCount : function(){
46315          return this.items.length;
46316      },
46317
46318     /**
46319      * Resizes all the tabs to the passed width
46320      * @param {Number} The new width
46321      */
46322     setTabWidth : function(width){
46323         this.currentTabWidth = width;
46324         for(var i = 0, len = this.items.length; i < len; i++) {
46325                 if(!this.items[i].isHidden()) {
46326                 this.items[i].setWidth(width);
46327             }
46328         }
46329     },
46330
46331     /**
46332      * Destroys this TabPanel
46333      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46334      */
46335     destroy : function(removeEl){
46336         Roo.EventManager.removeResizeListener(this.onResize, this);
46337         for(var i = 0, len = this.items.length; i < len; i++){
46338             this.items[i].purgeListeners();
46339         }
46340         if(removeEl === true){
46341             this.el.update("");
46342             this.el.remove();
46343         }
46344     },
46345     
46346     createStrip : function(container)
46347     {
46348         var strip = document.createElement("nav");
46349         strip.className = Roo.bootstrap.version == 4 ?
46350             "navbar-light bg-light" : 
46351             "navbar navbar-default"; //"x-tabs-wrap";
46352         container.appendChild(strip);
46353         return strip;
46354     },
46355     
46356     createStripList : function(strip)
46357     {
46358         // div wrapper for retard IE
46359         // returns the "tr" element.
46360         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46361         //'<div class="x-tabs-strip-wrap">'+
46362           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46363           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46364         return strip.firstChild; //.firstChild.firstChild.firstChild;
46365     },
46366     createBody : function(container)
46367     {
46368         var body = document.createElement("div");
46369         Roo.id(body, "tab-body");
46370         //Roo.fly(body).addClass("x-tabs-body");
46371         Roo.fly(body).addClass("tab-content");
46372         container.appendChild(body);
46373         return body;
46374     },
46375     createItemBody :function(bodyEl, id){
46376         var body = Roo.getDom(id);
46377         if(!body){
46378             body = document.createElement("div");
46379             body.id = id;
46380         }
46381         //Roo.fly(body).addClass("x-tabs-item-body");
46382         Roo.fly(body).addClass("tab-pane");
46383          bodyEl.insertBefore(body, bodyEl.firstChild);
46384         return body;
46385     },
46386     /** @private */
46387     createStripElements :  function(stripEl, text, closable, tpl)
46388     {
46389         var td = document.createElement("li"); // was td..
46390         td.className = 'nav-item';
46391         
46392         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46393         
46394         
46395         stripEl.appendChild(td);
46396         /*if(closable){
46397             td.className = "x-tabs-closable";
46398             if(!this.closeTpl){
46399                 this.closeTpl = new Roo.Template(
46400                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46401                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46402                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
46403                 );
46404             }
46405             var el = this.closeTpl.overwrite(td, {"text": text});
46406             var close = el.getElementsByTagName("div")[0];
46407             var inner = el.getElementsByTagName("em")[0];
46408             return {"el": el, "close": close, "inner": inner};
46409         } else {
46410         */
46411         // not sure what this is..
46412 //            if(!this.tabTpl){
46413                 //this.tabTpl = new Roo.Template(
46414                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46415                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46416                 //);
46417 //                this.tabTpl = new Roo.Template(
46418 //                   '<a href="#">' +
46419 //                   '<span unselectable="on"' +
46420 //                            (this.disableTooltips ? '' : ' title="{text}"') +
46421 //                            ' >{text}</span></a>'
46422 //                );
46423 //                
46424 //            }
46425
46426
46427             var template = tpl || this.tabTpl || false;
46428             
46429             if(!template){
46430                 template =  new Roo.Template(
46431                         Roo.bootstrap.version == 4 ? 
46432                             (
46433                                 '<a class="nav-link" href="#" unselectable="on"' +
46434                                      (this.disableTooltips ? '' : ' title="{text}"') +
46435                                      ' >{text}</a>'
46436                             ) : (
46437                                 '<a class="nav-link" href="#">' +
46438                                 '<span unselectable="on"' +
46439                                          (this.disableTooltips ? '' : ' title="{text}"') +
46440                                     ' >{text}</span></a>'
46441                             )
46442                 );
46443             }
46444             
46445             switch (typeof(template)) {
46446                 case 'object' :
46447                     break;
46448                 case 'string' :
46449                     template = new Roo.Template(template);
46450                     break;
46451                 default :
46452                     break;
46453             }
46454             
46455             var el = template.overwrite(td, {"text": text});
46456             
46457             var inner = el.getElementsByTagName("span")[0];
46458             
46459             return {"el": el, "inner": inner};
46460             
46461     }
46462         
46463     
46464 });
46465
46466 /**
46467  * @class Roo.TabPanelItem
46468  * @extends Roo.util.Observable
46469  * Represents an individual item (tab plus body) in a TabPanel.
46470  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46471  * @param {String} id The id of this TabPanelItem
46472  * @param {String} text The text for the tab of this TabPanelItem
46473  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46474  */
46475 Roo.bootstrap.panel.TabItem = function(config){
46476     /**
46477      * The {@link Roo.TabPanel} this TabPanelItem belongs to
46478      * @type Roo.TabPanel
46479      */
46480     this.tabPanel = config.panel;
46481     /**
46482      * The id for this TabPanelItem
46483      * @type String
46484      */
46485     this.id = config.id;
46486     /** @private */
46487     this.disabled = false;
46488     /** @private */
46489     this.text = config.text;
46490     /** @private */
46491     this.loaded = false;
46492     this.closable = config.closable;
46493
46494     /**
46495      * The body element for this TabPanelItem.
46496      * @type Roo.Element
46497      */
46498     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46499     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46500     this.bodyEl.setStyle("display", "block");
46501     this.bodyEl.setStyle("zoom", "1");
46502     //this.hideAction();
46503
46504     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46505     /** @private */
46506     this.el = Roo.get(els.el);
46507     this.inner = Roo.get(els.inner, true);
46508      this.textEl = Roo.bootstrap.version == 4 ?
46509         this.el : Roo.get(this.el.dom.firstChild, true);
46510
46511     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46512     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46513
46514     
46515 //    this.el.on("mousedown", this.onTabMouseDown, this);
46516     this.el.on("click", this.onTabClick, this);
46517     /** @private */
46518     if(config.closable){
46519         var c = Roo.get(els.close, true);
46520         c.dom.title = this.closeText;
46521         c.addClassOnOver("close-over");
46522         c.on("click", this.closeClick, this);
46523      }
46524
46525     this.addEvents({
46526          /**
46527          * @event activate
46528          * Fires when this tab becomes the active tab.
46529          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46530          * @param {Roo.TabPanelItem} this
46531          */
46532         "activate": true,
46533         /**
46534          * @event beforeclose
46535          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46536          * @param {Roo.TabPanelItem} this
46537          * @param {Object} e Set cancel to true on this object to cancel the close.
46538          */
46539         "beforeclose": true,
46540         /**
46541          * @event close
46542          * Fires when this tab is closed.
46543          * @param {Roo.TabPanelItem} this
46544          */
46545          "close": true,
46546         /**
46547          * @event deactivate
46548          * Fires when this tab is no longer the active tab.
46549          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46550          * @param {Roo.TabPanelItem} this
46551          */
46552          "deactivate" : true
46553     });
46554     this.hidden = false;
46555
46556     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46557 };
46558
46559 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46560            {
46561     purgeListeners : function(){
46562        Roo.util.Observable.prototype.purgeListeners.call(this);
46563        this.el.removeAllListeners();
46564     },
46565     /**
46566      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46567      */
46568     show : function(){
46569         this.status_node.addClass("active");
46570         this.showAction();
46571         if(Roo.isOpera){
46572             this.tabPanel.stripWrap.repaint();
46573         }
46574         this.fireEvent("activate", this.tabPanel, this);
46575     },
46576
46577     /**
46578      * Returns true if this tab is the active tab.
46579      * @return {Boolean}
46580      */
46581     isActive : function(){
46582         return this.tabPanel.getActiveTab() == this;
46583     },
46584
46585     /**
46586      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46587      */
46588     hide : function(){
46589         this.status_node.removeClass("active");
46590         this.hideAction();
46591         this.fireEvent("deactivate", this.tabPanel, this);
46592     },
46593
46594     hideAction : function(){
46595         this.bodyEl.hide();
46596         this.bodyEl.setStyle("position", "absolute");
46597         this.bodyEl.setLeft("-20000px");
46598         this.bodyEl.setTop("-20000px");
46599     },
46600
46601     showAction : function(){
46602         this.bodyEl.setStyle("position", "relative");
46603         this.bodyEl.setTop("");
46604         this.bodyEl.setLeft("");
46605         this.bodyEl.show();
46606     },
46607
46608     /**
46609      * Set the tooltip for the tab.
46610      * @param {String} tooltip The tab's tooltip
46611      */
46612     setTooltip : function(text){
46613         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46614             this.textEl.dom.qtip = text;
46615             this.textEl.dom.removeAttribute('title');
46616         }else{
46617             this.textEl.dom.title = text;
46618         }
46619     },
46620
46621     onTabClick : function(e){
46622         e.preventDefault();
46623         this.tabPanel.activate(this.id);
46624     },
46625
46626     onTabMouseDown : function(e){
46627         e.preventDefault();
46628         this.tabPanel.activate(this.id);
46629     },
46630 /*
46631     getWidth : function(){
46632         return this.inner.getWidth();
46633     },
46634
46635     setWidth : function(width){
46636         var iwidth = width - this.linode.getPadding("lr");
46637         this.inner.setWidth(iwidth);
46638         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46639         this.linode.setWidth(width);
46640     },
46641 */
46642     /**
46643      * Show or hide the tab
46644      * @param {Boolean} hidden True to hide or false to show.
46645      */
46646     setHidden : function(hidden){
46647         this.hidden = hidden;
46648         this.linode.setStyle("display", hidden ? "none" : "");
46649     },
46650
46651     /**
46652      * Returns true if this tab is "hidden"
46653      * @return {Boolean}
46654      */
46655     isHidden : function(){
46656         return this.hidden;
46657     },
46658
46659     /**
46660      * Returns the text for this tab
46661      * @return {String}
46662      */
46663     getText : function(){
46664         return this.text;
46665     },
46666     /*
46667     autoSize : function(){
46668         //this.el.beginMeasure();
46669         this.textEl.setWidth(1);
46670         /*
46671          *  #2804 [new] Tabs in Roojs
46672          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46673          */
46674         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46675         //this.el.endMeasure();
46676     //},
46677
46678     /**
46679      * Sets the text for the tab (Note: this also sets the tooltip text)
46680      * @param {String} text The tab's text and tooltip
46681      */
46682     setText : function(text){
46683         this.text = text;
46684         this.textEl.update(text);
46685         this.setTooltip(text);
46686         //if(!this.tabPanel.resizeTabs){
46687         //    this.autoSize();
46688         //}
46689     },
46690     /**
46691      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46692      */
46693     activate : function(){
46694         this.tabPanel.activate(this.id);
46695     },
46696
46697     /**
46698      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46699      */
46700     disable : function(){
46701         if(this.tabPanel.active != this){
46702             this.disabled = true;
46703             this.status_node.addClass("disabled");
46704         }
46705     },
46706
46707     /**
46708      * Enables this TabPanelItem if it was previously disabled.
46709      */
46710     enable : function(){
46711         this.disabled = false;
46712         this.status_node.removeClass("disabled");
46713     },
46714
46715     /**
46716      * Sets the content for this TabPanelItem.
46717      * @param {String} content The content
46718      * @param {Boolean} loadScripts true to look for and load scripts
46719      */
46720     setContent : function(content, loadScripts){
46721         this.bodyEl.update(content, loadScripts);
46722     },
46723
46724     /**
46725      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46726      * @return {Roo.UpdateManager} The UpdateManager
46727      */
46728     getUpdateManager : function(){
46729         return this.bodyEl.getUpdateManager();
46730     },
46731
46732     /**
46733      * Set a URL to be used to load the content for this TabPanelItem.
46734      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46735      * @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)
46736      * @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)
46737      * @return {Roo.UpdateManager} The UpdateManager
46738      */
46739     setUrl : function(url, params, loadOnce){
46740         if(this.refreshDelegate){
46741             this.un('activate', this.refreshDelegate);
46742         }
46743         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46744         this.on("activate", this.refreshDelegate);
46745         return this.bodyEl.getUpdateManager();
46746     },
46747
46748     /** @private */
46749     _handleRefresh : function(url, params, loadOnce){
46750         if(!loadOnce || !this.loaded){
46751             var updater = this.bodyEl.getUpdateManager();
46752             updater.update(url, params, this._setLoaded.createDelegate(this));
46753         }
46754     },
46755
46756     /**
46757      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
46758      *   Will fail silently if the setUrl method has not been called.
46759      *   This does not activate the panel, just updates its content.
46760      */
46761     refresh : function(){
46762         if(this.refreshDelegate){
46763            this.loaded = false;
46764            this.refreshDelegate();
46765         }
46766     },
46767
46768     /** @private */
46769     _setLoaded : function(){
46770         this.loaded = true;
46771     },
46772
46773     /** @private */
46774     closeClick : function(e){
46775         var o = {};
46776         e.stopEvent();
46777         this.fireEvent("beforeclose", this, o);
46778         if(o.cancel !== true){
46779             this.tabPanel.removeTab(this.id);
46780         }
46781     },
46782     /**
46783      * The text displayed in the tooltip for the close icon.
46784      * @type String
46785      */
46786     closeText : "Close this tab"
46787 });
46788 /**
46789 *    This script refer to:
46790 *    Title: International Telephone Input
46791 *    Author: Jack O'Connor
46792 *    Code version:  v12.1.12
46793 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46794 **/
46795
46796 Roo.bootstrap.form.PhoneInputData = function() {
46797     var d = [
46798       [
46799         "Afghanistan (‫افغانستان‬‎)",
46800         "af",
46801         "93"
46802       ],
46803       [
46804         "Albania (Shqipëri)",
46805         "al",
46806         "355"
46807       ],
46808       [
46809         "Algeria (‫الجزائر‬‎)",
46810         "dz",
46811         "213"
46812       ],
46813       [
46814         "American Samoa",
46815         "as",
46816         "1684"
46817       ],
46818       [
46819         "Andorra",
46820         "ad",
46821         "376"
46822       ],
46823       [
46824         "Angola",
46825         "ao",
46826         "244"
46827       ],
46828       [
46829         "Anguilla",
46830         "ai",
46831         "1264"
46832       ],
46833       [
46834         "Antigua and Barbuda",
46835         "ag",
46836         "1268"
46837       ],
46838       [
46839         "Argentina",
46840         "ar",
46841         "54"
46842       ],
46843       [
46844         "Armenia (Հայաստան)",
46845         "am",
46846         "374"
46847       ],
46848       [
46849         "Aruba",
46850         "aw",
46851         "297"
46852       ],
46853       [
46854         "Australia",
46855         "au",
46856         "61",
46857         0
46858       ],
46859       [
46860         "Austria (Österreich)",
46861         "at",
46862         "43"
46863       ],
46864       [
46865         "Azerbaijan (Azərbaycan)",
46866         "az",
46867         "994"
46868       ],
46869       [
46870         "Bahamas",
46871         "bs",
46872         "1242"
46873       ],
46874       [
46875         "Bahrain (‫البحرين‬‎)",
46876         "bh",
46877         "973"
46878       ],
46879       [
46880         "Bangladesh (বাংলাদেশ)",
46881         "bd",
46882         "880"
46883       ],
46884       [
46885         "Barbados",
46886         "bb",
46887         "1246"
46888       ],
46889       [
46890         "Belarus (Беларусь)",
46891         "by",
46892         "375"
46893       ],
46894       [
46895         "Belgium (België)",
46896         "be",
46897         "32"
46898       ],
46899       [
46900         "Belize",
46901         "bz",
46902         "501"
46903       ],
46904       [
46905         "Benin (Bénin)",
46906         "bj",
46907         "229"
46908       ],
46909       [
46910         "Bermuda",
46911         "bm",
46912         "1441"
46913       ],
46914       [
46915         "Bhutan (འབྲུག)",
46916         "bt",
46917         "975"
46918       ],
46919       [
46920         "Bolivia",
46921         "bo",
46922         "591"
46923       ],
46924       [
46925         "Bosnia and Herzegovina (Босна и Херцеговина)",
46926         "ba",
46927         "387"
46928       ],
46929       [
46930         "Botswana",
46931         "bw",
46932         "267"
46933       ],
46934       [
46935         "Brazil (Brasil)",
46936         "br",
46937         "55"
46938       ],
46939       [
46940         "British Indian Ocean Territory",
46941         "io",
46942         "246"
46943       ],
46944       [
46945         "British Virgin Islands",
46946         "vg",
46947         "1284"
46948       ],
46949       [
46950         "Brunei",
46951         "bn",
46952         "673"
46953       ],
46954       [
46955         "Bulgaria (България)",
46956         "bg",
46957         "359"
46958       ],
46959       [
46960         "Burkina Faso",
46961         "bf",
46962         "226"
46963       ],
46964       [
46965         "Burundi (Uburundi)",
46966         "bi",
46967         "257"
46968       ],
46969       [
46970         "Cambodia (កម្ពុជា)",
46971         "kh",
46972         "855"
46973       ],
46974       [
46975         "Cameroon (Cameroun)",
46976         "cm",
46977         "237"
46978       ],
46979       [
46980         "Canada",
46981         "ca",
46982         "1",
46983         1,
46984         ["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"]
46985       ],
46986       [
46987         "Cape Verde (Kabu Verdi)",
46988         "cv",
46989         "238"
46990       ],
46991       [
46992         "Caribbean Netherlands",
46993         "bq",
46994         "599",
46995         1
46996       ],
46997       [
46998         "Cayman Islands",
46999         "ky",
47000         "1345"
47001       ],
47002       [
47003         "Central African Republic (République centrafricaine)",
47004         "cf",
47005         "236"
47006       ],
47007       [
47008         "Chad (Tchad)",
47009         "td",
47010         "235"
47011       ],
47012       [
47013         "Chile",
47014         "cl",
47015         "56"
47016       ],
47017       [
47018         "China (中国)",
47019         "cn",
47020         "86"
47021       ],
47022       [
47023         "Christmas Island",
47024         "cx",
47025         "61",
47026         2
47027       ],
47028       [
47029         "Cocos (Keeling) Islands",
47030         "cc",
47031         "61",
47032         1
47033       ],
47034       [
47035         "Colombia",
47036         "co",
47037         "57"
47038       ],
47039       [
47040         "Comoros (‫جزر القمر‬‎)",
47041         "km",
47042         "269"
47043       ],
47044       [
47045         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
47046         "cd",
47047         "243"
47048       ],
47049       [
47050         "Congo (Republic) (Congo-Brazzaville)",
47051         "cg",
47052         "242"
47053       ],
47054       [
47055         "Cook Islands",
47056         "ck",
47057         "682"
47058       ],
47059       [
47060         "Costa Rica",
47061         "cr",
47062         "506"
47063       ],
47064       [
47065         "Côte d’Ivoire",
47066         "ci",
47067         "225"
47068       ],
47069       [
47070         "Croatia (Hrvatska)",
47071         "hr",
47072         "385"
47073       ],
47074       [
47075         "Cuba",
47076         "cu",
47077         "53"
47078       ],
47079       [
47080         "Curaçao",
47081         "cw",
47082         "599",
47083         0
47084       ],
47085       [
47086         "Cyprus (Κύπρος)",
47087         "cy",
47088         "357"
47089       ],
47090       [
47091         "Czech Republic (Česká republika)",
47092         "cz",
47093         "420"
47094       ],
47095       [
47096         "Denmark (Danmark)",
47097         "dk",
47098         "45"
47099       ],
47100       [
47101         "Djibouti",
47102         "dj",
47103         "253"
47104       ],
47105       [
47106         "Dominica",
47107         "dm",
47108         "1767"
47109       ],
47110       [
47111         "Dominican Republic (República Dominicana)",
47112         "do",
47113         "1",
47114         2,
47115         ["809", "829", "849"]
47116       ],
47117       [
47118         "Ecuador",
47119         "ec",
47120         "593"
47121       ],
47122       [
47123         "Egypt (‫مصر‬‎)",
47124         "eg",
47125         "20"
47126       ],
47127       [
47128         "El Salvador",
47129         "sv",
47130         "503"
47131       ],
47132       [
47133         "Equatorial Guinea (Guinea Ecuatorial)",
47134         "gq",
47135         "240"
47136       ],
47137       [
47138         "Eritrea",
47139         "er",
47140         "291"
47141       ],
47142       [
47143         "Estonia (Eesti)",
47144         "ee",
47145         "372"
47146       ],
47147       [
47148         "Ethiopia",
47149         "et",
47150         "251"
47151       ],
47152       [
47153         "Falkland Islands (Islas Malvinas)",
47154         "fk",
47155         "500"
47156       ],
47157       [
47158         "Faroe Islands (Føroyar)",
47159         "fo",
47160         "298"
47161       ],
47162       [
47163         "Fiji",
47164         "fj",
47165         "679"
47166       ],
47167       [
47168         "Finland (Suomi)",
47169         "fi",
47170         "358",
47171         0
47172       ],
47173       [
47174         "France",
47175         "fr",
47176         "33"
47177       ],
47178       [
47179         "French Guiana (Guyane française)",
47180         "gf",
47181         "594"
47182       ],
47183       [
47184         "French Polynesia (Polynésie française)",
47185         "pf",
47186         "689"
47187       ],
47188       [
47189         "Gabon",
47190         "ga",
47191         "241"
47192       ],
47193       [
47194         "Gambia",
47195         "gm",
47196         "220"
47197       ],
47198       [
47199         "Georgia (საქართველო)",
47200         "ge",
47201         "995"
47202       ],
47203       [
47204         "Germany (Deutschland)",
47205         "de",
47206         "49"
47207       ],
47208       [
47209         "Ghana (Gaana)",
47210         "gh",
47211         "233"
47212       ],
47213       [
47214         "Gibraltar",
47215         "gi",
47216         "350"
47217       ],
47218       [
47219         "Greece (Ελλάδα)",
47220         "gr",
47221         "30"
47222       ],
47223       [
47224         "Greenland (Kalaallit Nunaat)",
47225         "gl",
47226         "299"
47227       ],
47228       [
47229         "Grenada",
47230         "gd",
47231         "1473"
47232       ],
47233       [
47234         "Guadeloupe",
47235         "gp",
47236         "590",
47237         0
47238       ],
47239       [
47240         "Guam",
47241         "gu",
47242         "1671"
47243       ],
47244       [
47245         "Guatemala",
47246         "gt",
47247         "502"
47248       ],
47249       [
47250         "Guernsey",
47251         "gg",
47252         "44",
47253         1
47254       ],
47255       [
47256         "Guinea (Guinée)",
47257         "gn",
47258         "224"
47259       ],
47260       [
47261         "Guinea-Bissau (Guiné Bissau)",
47262         "gw",
47263         "245"
47264       ],
47265       [
47266         "Guyana",
47267         "gy",
47268         "592"
47269       ],
47270       [
47271         "Haiti",
47272         "ht",
47273         "509"
47274       ],
47275       [
47276         "Honduras",
47277         "hn",
47278         "504"
47279       ],
47280       [
47281         "Hong Kong (香港)",
47282         "hk",
47283         "852"
47284       ],
47285       [
47286         "Hungary (Magyarország)",
47287         "hu",
47288         "36"
47289       ],
47290       [
47291         "Iceland (Ísland)",
47292         "is",
47293         "354"
47294       ],
47295       [
47296         "India (भारत)",
47297         "in",
47298         "91"
47299       ],
47300       [
47301         "Indonesia",
47302         "id",
47303         "62"
47304       ],
47305       [
47306         "Iran (‫ایران‬‎)",
47307         "ir",
47308         "98"
47309       ],
47310       [
47311         "Iraq (‫العراق‬‎)",
47312         "iq",
47313         "964"
47314       ],
47315       [
47316         "Ireland",
47317         "ie",
47318         "353"
47319       ],
47320       [
47321         "Isle of Man",
47322         "im",
47323         "44",
47324         2
47325       ],
47326       [
47327         "Israel (‫ישראל‬‎)",
47328         "il",
47329         "972"
47330       ],
47331       [
47332         "Italy (Italia)",
47333         "it",
47334         "39",
47335         0
47336       ],
47337       [
47338         "Jamaica",
47339         "jm",
47340         "1876"
47341       ],
47342       [
47343         "Japan (日本)",
47344         "jp",
47345         "81"
47346       ],
47347       [
47348         "Jersey",
47349         "je",
47350         "44",
47351         3
47352       ],
47353       [
47354         "Jordan (‫الأردن‬‎)",
47355         "jo",
47356         "962"
47357       ],
47358       [
47359         "Kazakhstan (Казахстан)",
47360         "kz",
47361         "7",
47362         1
47363       ],
47364       [
47365         "Kenya",
47366         "ke",
47367         "254"
47368       ],
47369       [
47370         "Kiribati",
47371         "ki",
47372         "686"
47373       ],
47374       [
47375         "Kosovo",
47376         "xk",
47377         "383"
47378       ],
47379       [
47380         "Kuwait (‫الكويت‬‎)",
47381         "kw",
47382         "965"
47383       ],
47384       [
47385         "Kyrgyzstan (Кыргызстан)",
47386         "kg",
47387         "996"
47388       ],
47389       [
47390         "Laos (ລາວ)",
47391         "la",
47392         "856"
47393       ],
47394       [
47395         "Latvia (Latvija)",
47396         "lv",
47397         "371"
47398       ],
47399       [
47400         "Lebanon (‫لبنان‬‎)",
47401         "lb",
47402         "961"
47403       ],
47404       [
47405         "Lesotho",
47406         "ls",
47407         "266"
47408       ],
47409       [
47410         "Liberia",
47411         "lr",
47412         "231"
47413       ],
47414       [
47415         "Libya (‫ليبيا‬‎)",
47416         "ly",
47417         "218"
47418       ],
47419       [
47420         "Liechtenstein",
47421         "li",
47422         "423"
47423       ],
47424       [
47425         "Lithuania (Lietuva)",
47426         "lt",
47427         "370"
47428       ],
47429       [
47430         "Luxembourg",
47431         "lu",
47432         "352"
47433       ],
47434       [
47435         "Macau (澳門)",
47436         "mo",
47437         "853"
47438       ],
47439       [
47440         "Macedonia (FYROM) (Македонија)",
47441         "mk",
47442         "389"
47443       ],
47444       [
47445         "Madagascar (Madagasikara)",
47446         "mg",
47447         "261"
47448       ],
47449       [
47450         "Malawi",
47451         "mw",
47452         "265"
47453       ],
47454       [
47455         "Malaysia",
47456         "my",
47457         "60"
47458       ],
47459       [
47460         "Maldives",
47461         "mv",
47462         "960"
47463       ],
47464       [
47465         "Mali",
47466         "ml",
47467         "223"
47468       ],
47469       [
47470         "Malta",
47471         "mt",
47472         "356"
47473       ],
47474       [
47475         "Marshall Islands",
47476         "mh",
47477         "692"
47478       ],
47479       [
47480         "Martinique",
47481         "mq",
47482         "596"
47483       ],
47484       [
47485         "Mauritania (‫موريتانيا‬‎)",
47486         "mr",
47487         "222"
47488       ],
47489       [
47490         "Mauritius (Moris)",
47491         "mu",
47492         "230"
47493       ],
47494       [
47495         "Mayotte",
47496         "yt",
47497         "262",
47498         1
47499       ],
47500       [
47501         "Mexico (México)",
47502         "mx",
47503         "52"
47504       ],
47505       [
47506         "Micronesia",
47507         "fm",
47508         "691"
47509       ],
47510       [
47511         "Moldova (Republica Moldova)",
47512         "md",
47513         "373"
47514       ],
47515       [
47516         "Monaco",
47517         "mc",
47518         "377"
47519       ],
47520       [
47521         "Mongolia (Монгол)",
47522         "mn",
47523         "976"
47524       ],
47525       [
47526         "Montenegro (Crna Gora)",
47527         "me",
47528         "382"
47529       ],
47530       [
47531         "Montserrat",
47532         "ms",
47533         "1664"
47534       ],
47535       [
47536         "Morocco (‫المغرب‬‎)",
47537         "ma",
47538         "212",
47539         0
47540       ],
47541       [
47542         "Mozambique (Moçambique)",
47543         "mz",
47544         "258"
47545       ],
47546       [
47547         "Myanmar (Burma) (မြန်မာ)",
47548         "mm",
47549         "95"
47550       ],
47551       [
47552         "Namibia (Namibië)",
47553         "na",
47554         "264"
47555       ],
47556       [
47557         "Nauru",
47558         "nr",
47559         "674"
47560       ],
47561       [
47562         "Nepal (नेपाल)",
47563         "np",
47564         "977"
47565       ],
47566       [
47567         "Netherlands (Nederland)",
47568         "nl",
47569         "31"
47570       ],
47571       [
47572         "New Caledonia (Nouvelle-Calédonie)",
47573         "nc",
47574         "687"
47575       ],
47576       [
47577         "New Zealand",
47578         "nz",
47579         "64"
47580       ],
47581       [
47582         "Nicaragua",
47583         "ni",
47584         "505"
47585       ],
47586       [
47587         "Niger (Nijar)",
47588         "ne",
47589         "227"
47590       ],
47591       [
47592         "Nigeria",
47593         "ng",
47594         "234"
47595       ],
47596       [
47597         "Niue",
47598         "nu",
47599         "683"
47600       ],
47601       [
47602         "Norfolk Island",
47603         "nf",
47604         "672"
47605       ],
47606       [
47607         "North Korea (조선 민주주의 인민 공화국)",
47608         "kp",
47609         "850"
47610       ],
47611       [
47612         "Northern Mariana Islands",
47613         "mp",
47614         "1670"
47615       ],
47616       [
47617         "Norway (Norge)",
47618         "no",
47619         "47",
47620         0
47621       ],
47622       [
47623         "Oman (‫عُمان‬‎)",
47624         "om",
47625         "968"
47626       ],
47627       [
47628         "Pakistan (‫پاکستان‬‎)",
47629         "pk",
47630         "92"
47631       ],
47632       [
47633         "Palau",
47634         "pw",
47635         "680"
47636       ],
47637       [
47638         "Palestine (‫فلسطين‬‎)",
47639         "ps",
47640         "970"
47641       ],
47642       [
47643         "Panama (Panamá)",
47644         "pa",
47645         "507"
47646       ],
47647       [
47648         "Papua New Guinea",
47649         "pg",
47650         "675"
47651       ],
47652       [
47653         "Paraguay",
47654         "py",
47655         "595"
47656       ],
47657       [
47658         "Peru (Perú)",
47659         "pe",
47660         "51"
47661       ],
47662       [
47663         "Philippines",
47664         "ph",
47665         "63"
47666       ],
47667       [
47668         "Poland (Polska)",
47669         "pl",
47670         "48"
47671       ],
47672       [
47673         "Portugal",
47674         "pt",
47675         "351"
47676       ],
47677       [
47678         "Puerto Rico",
47679         "pr",
47680         "1",
47681         3,
47682         ["787", "939"]
47683       ],
47684       [
47685         "Qatar (‫قطر‬‎)",
47686         "qa",
47687         "974"
47688       ],
47689       [
47690         "Réunion (La Réunion)",
47691         "re",
47692         "262",
47693         0
47694       ],
47695       [
47696         "Romania (România)",
47697         "ro",
47698         "40"
47699       ],
47700       [
47701         "Russia (Россия)",
47702         "ru",
47703         "7",
47704         0
47705       ],
47706       [
47707         "Rwanda",
47708         "rw",
47709         "250"
47710       ],
47711       [
47712         "Saint Barthélemy",
47713         "bl",
47714         "590",
47715         1
47716       ],
47717       [
47718         "Saint Helena",
47719         "sh",
47720         "290"
47721       ],
47722       [
47723         "Saint Kitts and Nevis",
47724         "kn",
47725         "1869"
47726       ],
47727       [
47728         "Saint Lucia",
47729         "lc",
47730         "1758"
47731       ],
47732       [
47733         "Saint Martin (Saint-Martin (partie française))",
47734         "mf",
47735         "590",
47736         2
47737       ],
47738       [
47739         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47740         "pm",
47741         "508"
47742       ],
47743       [
47744         "Saint Vincent and the Grenadines",
47745         "vc",
47746         "1784"
47747       ],
47748       [
47749         "Samoa",
47750         "ws",
47751         "685"
47752       ],
47753       [
47754         "San Marino",
47755         "sm",
47756         "378"
47757       ],
47758       [
47759         "São Tomé and Príncipe (São Tomé e Príncipe)",
47760         "st",
47761         "239"
47762       ],
47763       [
47764         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
47765         "sa",
47766         "966"
47767       ],
47768       [
47769         "Senegal (Sénégal)",
47770         "sn",
47771         "221"
47772       ],
47773       [
47774         "Serbia (Србија)",
47775         "rs",
47776         "381"
47777       ],
47778       [
47779         "Seychelles",
47780         "sc",
47781         "248"
47782       ],
47783       [
47784         "Sierra Leone",
47785         "sl",
47786         "232"
47787       ],
47788       [
47789         "Singapore",
47790         "sg",
47791         "65"
47792       ],
47793       [
47794         "Sint Maarten",
47795         "sx",
47796         "1721"
47797       ],
47798       [
47799         "Slovakia (Slovensko)",
47800         "sk",
47801         "421"
47802       ],
47803       [
47804         "Slovenia (Slovenija)",
47805         "si",
47806         "386"
47807       ],
47808       [
47809         "Solomon Islands",
47810         "sb",
47811         "677"
47812       ],
47813       [
47814         "Somalia (Soomaaliya)",
47815         "so",
47816         "252"
47817       ],
47818       [
47819         "South Africa",
47820         "za",
47821         "27"
47822       ],
47823       [
47824         "South Korea (대한민국)",
47825         "kr",
47826         "82"
47827       ],
47828       [
47829         "South Sudan (‫جنوب السودان‬‎)",
47830         "ss",
47831         "211"
47832       ],
47833       [
47834         "Spain (España)",
47835         "es",
47836         "34"
47837       ],
47838       [
47839         "Sri Lanka (ශ්‍රී ලංකාව)",
47840         "lk",
47841         "94"
47842       ],
47843       [
47844         "Sudan (‫السودان‬‎)",
47845         "sd",
47846         "249"
47847       ],
47848       [
47849         "Suriname",
47850         "sr",
47851         "597"
47852       ],
47853       [
47854         "Svalbard and Jan Mayen",
47855         "sj",
47856         "47",
47857         1
47858       ],
47859       [
47860         "Swaziland",
47861         "sz",
47862         "268"
47863       ],
47864       [
47865         "Sweden (Sverige)",
47866         "se",
47867         "46"
47868       ],
47869       [
47870         "Switzerland (Schweiz)",
47871         "ch",
47872         "41"
47873       ],
47874       [
47875         "Syria (‫سوريا‬‎)",
47876         "sy",
47877         "963"
47878       ],
47879       [
47880         "Taiwan (台灣)",
47881         "tw",
47882         "886"
47883       ],
47884       [
47885         "Tajikistan",
47886         "tj",
47887         "992"
47888       ],
47889       [
47890         "Tanzania",
47891         "tz",
47892         "255"
47893       ],
47894       [
47895         "Thailand (ไทย)",
47896         "th",
47897         "66"
47898       ],
47899       [
47900         "Timor-Leste",
47901         "tl",
47902         "670"
47903       ],
47904       [
47905         "Togo",
47906         "tg",
47907         "228"
47908       ],
47909       [
47910         "Tokelau",
47911         "tk",
47912         "690"
47913       ],
47914       [
47915         "Tonga",
47916         "to",
47917         "676"
47918       ],
47919       [
47920         "Trinidad and Tobago",
47921         "tt",
47922         "1868"
47923       ],
47924       [
47925         "Tunisia (‫تونس‬‎)",
47926         "tn",
47927         "216"
47928       ],
47929       [
47930         "Turkey (Türkiye)",
47931         "tr",
47932         "90"
47933       ],
47934       [
47935         "Turkmenistan",
47936         "tm",
47937         "993"
47938       ],
47939       [
47940         "Turks and Caicos Islands",
47941         "tc",
47942         "1649"
47943       ],
47944       [
47945         "Tuvalu",
47946         "tv",
47947         "688"
47948       ],
47949       [
47950         "U.S. Virgin Islands",
47951         "vi",
47952         "1340"
47953       ],
47954       [
47955         "Uganda",
47956         "ug",
47957         "256"
47958       ],
47959       [
47960         "Ukraine (Україна)",
47961         "ua",
47962         "380"
47963       ],
47964       [
47965         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
47966         "ae",
47967         "971"
47968       ],
47969       [
47970         "United Kingdom",
47971         "gb",
47972         "44",
47973         0
47974       ],
47975       [
47976         "United States",
47977         "us",
47978         "1",
47979         0
47980       ],
47981       [
47982         "Uruguay",
47983         "uy",
47984         "598"
47985       ],
47986       [
47987         "Uzbekistan (Oʻzbekiston)",
47988         "uz",
47989         "998"
47990       ],
47991       [
47992         "Vanuatu",
47993         "vu",
47994         "678"
47995       ],
47996       [
47997         "Vatican City (Città del Vaticano)",
47998         "va",
47999         "39",
48000         1
48001       ],
48002       [
48003         "Venezuela",
48004         "ve",
48005         "58"
48006       ],
48007       [
48008         "Vietnam (Việt Nam)",
48009         "vn",
48010         "84"
48011       ],
48012       [
48013         "Wallis and Futuna (Wallis-et-Futuna)",
48014         "wf",
48015         "681"
48016       ],
48017       [
48018         "Western Sahara (‫الصحراء الغربية‬‎)",
48019         "eh",
48020         "212",
48021         1
48022       ],
48023       [
48024         "Yemen (‫اليمن‬‎)",
48025         "ye",
48026         "967"
48027       ],
48028       [
48029         "Zambia",
48030         "zm",
48031         "260"
48032       ],
48033       [
48034         "Zimbabwe",
48035         "zw",
48036         "263"
48037       ],
48038       [
48039         "Åland Islands",
48040         "ax",
48041         "358",
48042         1
48043       ]
48044   ];
48045   
48046   return d;
48047 }/**
48048 *    This script refer to:
48049 *    Title: International Telephone Input
48050 *    Author: Jack O'Connor
48051 *    Code version:  v12.1.12
48052 *    Availability: https://github.com/jackocnr/intl-tel-input.git
48053 **/
48054
48055 /**
48056  * @class Roo.bootstrap.form.PhoneInput
48057  * @extends Roo.bootstrap.form.TriggerField
48058  * An input with International dial-code selection
48059  
48060  * @cfg {String} defaultDialCode default '+852'
48061  * @cfg {Array} preferedCountries default []
48062   
48063  * @constructor
48064  * Create a new PhoneInput.
48065  * @param {Object} config Configuration options
48066  */
48067
48068 Roo.bootstrap.form.PhoneInput = function(config) {
48069     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48070 };
48071
48072 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48073         /**
48074         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48075         */
48076         listWidth: undefined,
48077         
48078         selectedClass: 'active',
48079         
48080         invalidClass : "has-warning",
48081         
48082         validClass: 'has-success',
48083         
48084         allowed: '0123456789',
48085         
48086         max_length: 15,
48087         
48088         /**
48089          * @cfg {String} defaultDialCode The default dial code when initializing the input
48090          */
48091         defaultDialCode: '+852',
48092         
48093         /**
48094          * @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
48095          */
48096         preferedCountries: false,
48097         
48098         getAutoCreate : function()
48099         {
48100             var data = Roo.bootstrap.form.PhoneInputData();
48101             var align = this.labelAlign || this.parentLabelAlign();
48102             var id = Roo.id();
48103             
48104             this.allCountries = [];
48105             this.dialCodeMapping = [];
48106             
48107             for (var i = 0; i < data.length; i++) {
48108               var c = data[i];
48109               this.allCountries[i] = {
48110                 name: c[0],
48111                 iso2: c[1],
48112                 dialCode: c[2],
48113                 priority: c[3] || 0,
48114                 areaCodes: c[4] || null
48115               };
48116               this.dialCodeMapping[c[2]] = {
48117                   name: c[0],
48118                   iso2: c[1],
48119                   priority: c[3] || 0,
48120                   areaCodes: c[4] || null
48121               };
48122             }
48123             
48124             var cfg = {
48125                 cls: 'form-group',
48126                 cn: []
48127             };
48128             
48129             var input =  {
48130                 tag: 'input',
48131                 id : id,
48132                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48133                 maxlength: this.max_length,
48134                 cls : 'form-control tel-input',
48135                 autocomplete: 'new-password'
48136             };
48137             
48138             var hiddenInput = {
48139                 tag: 'input',
48140                 type: 'hidden',
48141                 cls: 'hidden-tel-input'
48142             };
48143             
48144             if (this.name) {
48145                 hiddenInput.name = this.name;
48146             }
48147             
48148             if (this.disabled) {
48149                 input.disabled = true;
48150             }
48151             
48152             var flag_container = {
48153                 tag: 'div',
48154                 cls: 'flag-box',
48155                 cn: [
48156                     {
48157                         tag: 'div',
48158                         cls: 'flag'
48159                     },
48160                     {
48161                         tag: 'div',
48162                         cls: 'caret'
48163                     }
48164                 ]
48165             };
48166             
48167             var box = {
48168                 tag: 'div',
48169                 cls: this.hasFeedback ? 'has-feedback' : '',
48170                 cn: [
48171                     hiddenInput,
48172                     input,
48173                     {
48174                         tag: 'input',
48175                         cls: 'dial-code-holder',
48176                         disabled: true
48177                     }
48178                 ]
48179             };
48180             
48181             var container = {
48182                 cls: 'roo-select2-container input-group',
48183                 cn: [
48184                     flag_container,
48185                     box
48186                 ]
48187             };
48188             
48189             if (this.fieldLabel.length) {
48190                 var indicator = {
48191                     tag: 'i',
48192                     tooltip: 'This field is required'
48193                 };
48194                 
48195                 var label = {
48196                     tag: 'label',
48197                     'for':  id,
48198                     cls: 'control-label',
48199                     cn: []
48200                 };
48201                 
48202                 var label_text = {
48203                     tag: 'span',
48204                     html: this.fieldLabel
48205                 };
48206                 
48207                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48208                 label.cn = [
48209                     indicator,
48210                     label_text
48211                 ];
48212                 
48213                 if(this.indicatorpos == 'right') {
48214                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48215                     label.cn = [
48216                         label_text,
48217                         indicator
48218                     ];
48219                 }
48220                 
48221                 if(align == 'left') {
48222                     container = {
48223                         tag: 'div',
48224                         cn: [
48225                             container
48226                         ]
48227                     };
48228                     
48229                     if(this.labelWidth > 12){
48230                         label.style = "width: " + this.labelWidth + 'px';
48231                     }
48232                     if(this.labelWidth < 13 && this.labelmd == 0){
48233                         this.labelmd = this.labelWidth;
48234                     }
48235                     if(this.labellg > 0){
48236                         label.cls += ' col-lg-' + this.labellg;
48237                         input.cls += ' col-lg-' + (12 - this.labellg);
48238                     }
48239                     if(this.labelmd > 0){
48240                         label.cls += ' col-md-' + this.labelmd;
48241                         container.cls += ' col-md-' + (12 - this.labelmd);
48242                     }
48243                     if(this.labelsm > 0){
48244                         label.cls += ' col-sm-' + this.labelsm;
48245                         container.cls += ' col-sm-' + (12 - this.labelsm);
48246                     }
48247                     if(this.labelxs > 0){
48248                         label.cls += ' col-xs-' + this.labelxs;
48249                         container.cls += ' col-xs-' + (12 - this.labelxs);
48250                     }
48251                 }
48252             }
48253             
48254             cfg.cn = [
48255                 label,
48256                 container
48257             ];
48258             
48259             var settings = this;
48260             
48261             ['xs','sm','md','lg'].map(function(size){
48262                 if (settings[size]) {
48263                     cfg.cls += ' col-' + size + '-' + settings[size];
48264                 }
48265             });
48266             
48267             this.store = new Roo.data.Store({
48268                 proxy : new Roo.data.MemoryProxy({}),
48269                 reader : new Roo.data.JsonReader({
48270                     fields : [
48271                         {
48272                             'name' : 'name',
48273                             'type' : 'string'
48274                         },
48275                         {
48276                             'name' : 'iso2',
48277                             'type' : 'string'
48278                         },
48279                         {
48280                             'name' : 'dialCode',
48281                             'type' : 'string'
48282                         },
48283                         {
48284                             'name' : 'priority',
48285                             'type' : 'string'
48286                         },
48287                         {
48288                             'name' : 'areaCodes',
48289                             'type' : 'string'
48290                         }
48291                     ]
48292                 })
48293             });
48294             
48295             if(!this.preferedCountries) {
48296                 this.preferedCountries = [
48297                     'hk',
48298                     'gb',
48299                     'us'
48300                 ];
48301             }
48302             
48303             var p = this.preferedCountries.reverse();
48304             
48305             if(p) {
48306                 for (var i = 0; i < p.length; i++) {
48307                     for (var j = 0; j < this.allCountries.length; j++) {
48308                         if(this.allCountries[j].iso2 == p[i]) {
48309                             var t = this.allCountries[j];
48310                             this.allCountries.splice(j,1);
48311                             this.allCountries.unshift(t);
48312                         }
48313                     } 
48314                 }
48315             }
48316             
48317             this.store.proxy.data = {
48318                 success: true,
48319                 data: this.allCountries
48320             };
48321             
48322             return cfg;
48323         },
48324         
48325         initEvents : function()
48326         {
48327             this.createList();
48328             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48329             
48330             this.indicator = this.indicatorEl();
48331             this.flag = this.flagEl();
48332             this.dialCodeHolder = this.dialCodeHolderEl();
48333             
48334             this.trigger = this.el.select('div.flag-box',true).first();
48335             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48336             
48337             var _this = this;
48338             
48339             (function(){
48340                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48341                 _this.list.setWidth(lw);
48342             }).defer(100);
48343             
48344             this.list.on('mouseover', this.onViewOver, this);
48345             this.list.on('mousemove', this.onViewMove, this);
48346             this.inputEl().on("keyup", this.onKeyUp, this);
48347             this.inputEl().on("keypress", this.onKeyPress, this);
48348             
48349             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48350
48351             this.view = new Roo.View(this.list, this.tpl, {
48352                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48353             });
48354             
48355             this.view.on('click', this.onViewClick, this);
48356             this.setValue(this.defaultDialCode);
48357         },
48358         
48359         onTriggerClick : function(e)
48360         {
48361             Roo.log('trigger click');
48362             if(this.disabled){
48363                 return;
48364             }
48365             
48366             if(this.isExpanded()){
48367                 this.collapse();
48368                 this.hasFocus = false;
48369             }else {
48370                 this.store.load({});
48371                 this.hasFocus = true;
48372                 this.expand();
48373             }
48374         },
48375         
48376         isExpanded : function()
48377         {
48378             return this.list.isVisible();
48379         },
48380         
48381         collapse : function()
48382         {
48383             if(!this.isExpanded()){
48384                 return;
48385             }
48386             this.list.hide();
48387             Roo.get(document).un('mousedown', this.collapseIf, this);
48388             Roo.get(document).un('mousewheel', this.collapseIf, this);
48389             this.fireEvent('collapse', this);
48390             this.validate();
48391         },
48392         
48393         expand : function()
48394         {
48395             Roo.log('expand');
48396
48397             if(this.isExpanded() || !this.hasFocus){
48398                 return;
48399             }
48400             
48401             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48402             this.list.setWidth(lw);
48403             
48404             this.list.show();
48405             this.restrictHeight();
48406             
48407             Roo.get(document).on('mousedown', this.collapseIf, this);
48408             Roo.get(document).on('mousewheel', this.collapseIf, this);
48409             
48410             this.fireEvent('expand', this);
48411         },
48412         
48413         restrictHeight : function()
48414         {
48415             this.list.alignTo(this.inputEl(), this.listAlign);
48416             this.list.alignTo(this.inputEl(), this.listAlign);
48417         },
48418         
48419         onViewOver : function(e, t)
48420         {
48421             if(this.inKeyMode){
48422                 return;
48423             }
48424             var item = this.view.findItemFromChild(t);
48425             
48426             if(item){
48427                 var index = this.view.indexOf(item);
48428                 this.select(index, false);
48429             }
48430         },
48431
48432         // private
48433         onViewClick : function(view, doFocus, el, e)
48434         {
48435             var index = this.view.getSelectedIndexes()[0];
48436             
48437             var r = this.store.getAt(index);
48438             
48439             if(r){
48440                 this.onSelect(r, index);
48441             }
48442             if(doFocus !== false && !this.blockFocus){
48443                 this.inputEl().focus();
48444             }
48445         },
48446         
48447         onViewMove : function(e, t)
48448         {
48449             this.inKeyMode = false;
48450         },
48451         
48452         select : function(index, scrollIntoView)
48453         {
48454             this.selectedIndex = index;
48455             this.view.select(index);
48456             if(scrollIntoView !== false){
48457                 var el = this.view.getNode(index);
48458                 if(el){
48459                     this.list.scrollChildIntoView(el, false);
48460                 }
48461             }
48462         },
48463         
48464         createList : function()
48465         {
48466             this.list = Roo.get(document.body).createChild({
48467                 tag: 'ul',
48468                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48469                 style: 'display:none'
48470             });
48471             
48472             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48473         },
48474         
48475         collapseIf : function(e)
48476         {
48477             var in_combo  = e.within(this.el);
48478             var in_list =  e.within(this.list);
48479             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48480             
48481             if (in_combo || in_list || is_list) {
48482                 return;
48483             }
48484             this.collapse();
48485         },
48486         
48487         onSelect : function(record, index)
48488         {
48489             if(this.fireEvent('beforeselect', this, record, index) !== false){
48490                 
48491                 this.setFlagClass(record.data.iso2);
48492                 this.setDialCode(record.data.dialCode);
48493                 this.hasFocus = false;
48494                 this.collapse();
48495                 this.fireEvent('select', this, record, index);
48496             }
48497         },
48498         
48499         flagEl : function()
48500         {
48501             var flag = this.el.select('div.flag',true).first();
48502             if(!flag){
48503                 return false;
48504             }
48505             return flag;
48506         },
48507         
48508         dialCodeHolderEl : function()
48509         {
48510             var d = this.el.select('input.dial-code-holder',true).first();
48511             if(!d){
48512                 return false;
48513             }
48514             return d;
48515         },
48516         
48517         setDialCode : function(v)
48518         {
48519             this.dialCodeHolder.dom.value = '+'+v;
48520         },
48521         
48522         setFlagClass : function(n)
48523         {
48524             this.flag.dom.className = 'flag '+n;
48525         },
48526         
48527         getValue : function()
48528         {
48529             var v = this.inputEl().getValue();
48530             if(this.dialCodeHolder) {
48531                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48532             }
48533             return v;
48534         },
48535         
48536         setValue : function(v)
48537         {
48538             var d = this.getDialCode(v);
48539             
48540             //invalid dial code
48541             if(v.length == 0 || !d || d.length == 0) {
48542                 if(this.rendered){
48543                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48544                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48545                 }
48546                 return;
48547             }
48548             
48549             //valid dial code
48550             this.setFlagClass(this.dialCodeMapping[d].iso2);
48551             this.setDialCode(d);
48552             this.inputEl().dom.value = v.replace('+'+d,'');
48553             this.hiddenEl().dom.value = this.getValue();
48554             
48555             this.validate();
48556         },
48557         
48558         getDialCode : function(v)
48559         {
48560             v = v ||  '';
48561             
48562             if (v.length == 0) {
48563                 return this.dialCodeHolder.dom.value;
48564             }
48565             
48566             var dialCode = "";
48567             if (v.charAt(0) != "+") {
48568                 return false;
48569             }
48570             var numericChars = "";
48571             for (var i = 1; i < v.length; i++) {
48572               var c = v.charAt(i);
48573               if (!isNaN(c)) {
48574                 numericChars += c;
48575                 if (this.dialCodeMapping[numericChars]) {
48576                   dialCode = v.substr(1, i);
48577                 }
48578                 if (numericChars.length == 4) {
48579                   break;
48580                 }
48581               }
48582             }
48583             return dialCode;
48584         },
48585         
48586         reset : function()
48587         {
48588             this.setValue(this.defaultDialCode);
48589             this.validate();
48590         },
48591         
48592         hiddenEl : function()
48593         {
48594             return this.el.select('input.hidden-tel-input',true).first();
48595         },
48596         
48597         // after setting val
48598         onKeyUp : function(e){
48599             this.setValue(this.getValue());
48600         },
48601         
48602         onKeyPress : function(e){
48603             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48604                 e.stopEvent();
48605             }
48606         }
48607         
48608 });
48609 /**
48610  * @class Roo.bootstrap.form.MoneyField
48611  * @extends Roo.bootstrap.form.ComboBox
48612  * Bootstrap MoneyField class
48613  * 
48614  * @constructor
48615  * Create a new MoneyField.
48616  * @param {Object} config Configuration options
48617  */
48618
48619 Roo.bootstrap.form.MoneyField = function(config) {
48620     
48621     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48622     
48623 };
48624
48625 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48626     
48627     /**
48628      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48629      */
48630     allowDecimals : true,
48631     /**
48632      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48633      */
48634     decimalSeparator : ".",
48635     /**
48636      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48637      */
48638     decimalPrecision : 0,
48639     /**
48640      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48641      */
48642     allowNegative : true,
48643     /**
48644      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48645      */
48646     allowZero: true,
48647     /**
48648      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48649      */
48650     minValue : Number.NEGATIVE_INFINITY,
48651     /**
48652      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48653      */
48654     maxValue : Number.MAX_VALUE,
48655     /**
48656      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48657      */
48658     minText : "The minimum value for this field is {0}",
48659     /**
48660      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48661      */
48662     maxText : "The maximum value for this field is {0}",
48663     /**
48664      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48665      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48666      */
48667     nanText : "{0} is not a valid number",
48668     /**
48669      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48670      */
48671     castInt : true,
48672     /**
48673      * @cfg {String} defaults currency of the MoneyField
48674      * value should be in lkey
48675      */
48676     defaultCurrency : false,
48677     /**
48678      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48679      */
48680     thousandsDelimiter : false,
48681     /**
48682      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48683      */
48684     max_length: false,
48685     
48686     inputlg : 9,
48687     inputmd : 9,
48688     inputsm : 9,
48689     inputxs : 6,
48690      /**
48691      * @cfg {Roo.data.Store} store  Store to lookup currency??
48692      */
48693     store : false,
48694     
48695     getAutoCreate : function()
48696     {
48697         var align = this.labelAlign || this.parentLabelAlign();
48698         
48699         var id = Roo.id();
48700
48701         var cfg = {
48702             cls: 'form-group',
48703             cn: []
48704         };
48705
48706         var input =  {
48707             tag: 'input',
48708             id : id,
48709             cls : 'form-control roo-money-amount-input',
48710             autocomplete: 'new-password'
48711         };
48712         
48713         var hiddenInput = {
48714             tag: 'input',
48715             type: 'hidden',
48716             id: Roo.id(),
48717             cls: 'hidden-number-input'
48718         };
48719         
48720         if(this.max_length) {
48721             input.maxlength = this.max_length; 
48722         }
48723         
48724         if (this.name) {
48725             hiddenInput.name = this.name;
48726         }
48727
48728         if (this.disabled) {
48729             input.disabled = true;
48730         }
48731
48732         var clg = 12 - this.inputlg;
48733         var cmd = 12 - this.inputmd;
48734         var csm = 12 - this.inputsm;
48735         var cxs = 12 - this.inputxs;
48736         
48737         var container = {
48738             tag : 'div',
48739             cls : 'row roo-money-field',
48740             cn : [
48741                 {
48742                     tag : 'div',
48743                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48744                     cn : [
48745                         {
48746                             tag : 'div',
48747                             cls: 'roo-select2-container input-group',
48748                             cn: [
48749                                 {
48750                                     tag : 'input',
48751                                     cls : 'form-control roo-money-currency-input',
48752                                     autocomplete: 'new-password',
48753                                     readOnly : 1,
48754                                     name : this.currencyName
48755                                 },
48756                                 {
48757                                     tag :'span',
48758                                     cls : 'input-group-addon',
48759                                     cn : [
48760                                         {
48761                                             tag: 'span',
48762                                             cls: 'caret'
48763                                         }
48764                                     ]
48765                                 }
48766                             ]
48767                         }
48768                     ]
48769                 },
48770                 {
48771                     tag : 'div',
48772                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48773                     cn : [
48774                         {
48775                             tag: 'div',
48776                             cls: this.hasFeedback ? 'has-feedback' : '',
48777                             cn: [
48778                                 input
48779                             ]
48780                         }
48781                     ]
48782                 }
48783             ]
48784             
48785         };
48786         
48787         if (this.fieldLabel.length) {
48788             var indicator = {
48789                 tag: 'i',
48790                 tooltip: 'This field is required'
48791             };
48792
48793             var label = {
48794                 tag: 'label',
48795                 'for':  id,
48796                 cls: 'control-label',
48797                 cn: []
48798             };
48799
48800             var label_text = {
48801                 tag: 'span',
48802                 html: this.fieldLabel
48803             };
48804
48805             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48806             label.cn = [
48807                 indicator,
48808                 label_text
48809             ];
48810
48811             if(this.indicatorpos == 'right') {
48812                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48813                 label.cn = [
48814                     label_text,
48815                     indicator
48816                 ];
48817             }
48818
48819             if(align == 'left') {
48820                 container = {
48821                     tag: 'div',
48822                     cn: [
48823                         container
48824                     ]
48825                 };
48826
48827                 if(this.labelWidth > 12){
48828                     label.style = "width: " + this.labelWidth + 'px';
48829                 }
48830                 if(this.labelWidth < 13 && this.labelmd == 0){
48831                     this.labelmd = this.labelWidth;
48832                 }
48833                 if(this.labellg > 0){
48834                     label.cls += ' col-lg-' + this.labellg;
48835                     input.cls += ' col-lg-' + (12 - this.labellg);
48836                 }
48837                 if(this.labelmd > 0){
48838                     label.cls += ' col-md-' + this.labelmd;
48839                     container.cls += ' col-md-' + (12 - this.labelmd);
48840                 }
48841                 if(this.labelsm > 0){
48842                     label.cls += ' col-sm-' + this.labelsm;
48843                     container.cls += ' col-sm-' + (12 - this.labelsm);
48844                 }
48845                 if(this.labelxs > 0){
48846                     label.cls += ' col-xs-' + this.labelxs;
48847                     container.cls += ' col-xs-' + (12 - this.labelxs);
48848                 }
48849             }
48850         }
48851
48852         cfg.cn = [
48853             label,
48854             container,
48855             hiddenInput
48856         ];
48857         
48858         var settings = this;
48859
48860         ['xs','sm','md','lg'].map(function(size){
48861             if (settings[size]) {
48862                 cfg.cls += ' col-' + size + '-' + settings[size];
48863             }
48864         });
48865         
48866         return cfg;
48867     },
48868     
48869     initEvents : function()
48870     {
48871         this.indicator = this.indicatorEl();
48872         
48873         this.initCurrencyEvent();
48874         
48875         this.initNumberEvent();
48876     },
48877     
48878     initCurrencyEvent : function()
48879     {
48880         if (!this.store) {
48881             throw "can not find store for combo";
48882         }
48883         
48884         this.store = Roo.factory(this.store, Roo.data);
48885         this.store.parent = this;
48886         
48887         this.createList();
48888         
48889         this.triggerEl = this.el.select('.input-group-addon', true).first();
48890         
48891         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48892         
48893         var _this = this;
48894         
48895         (function(){
48896             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48897             _this.list.setWidth(lw);
48898         }).defer(100);
48899         
48900         this.list.on('mouseover', this.onViewOver, this);
48901         this.list.on('mousemove', this.onViewMove, this);
48902         this.list.on('scroll', this.onViewScroll, this);
48903         
48904         if(!this.tpl){
48905             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48906         }
48907         
48908         this.view = new Roo.View(this.list, this.tpl, {
48909             singleSelect:true, store: this.store, selectedClass: this.selectedClass
48910         });
48911         
48912         this.view.on('click', this.onViewClick, this);
48913         
48914         this.store.on('beforeload', this.onBeforeLoad, this);
48915         this.store.on('load', this.onLoad, this);
48916         this.store.on('loadexception', this.onLoadException, this);
48917         
48918         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48919             "up" : function(e){
48920                 this.inKeyMode = true;
48921                 this.selectPrev();
48922             },
48923
48924             "down" : function(e){
48925                 if(!this.isExpanded()){
48926                     this.onTriggerClick();
48927                 }else{
48928                     this.inKeyMode = true;
48929                     this.selectNext();
48930                 }
48931             },
48932
48933             "enter" : function(e){
48934                 this.collapse();
48935                 
48936                 if(this.fireEvent("specialkey", this, e)){
48937                     this.onViewClick(false);
48938                 }
48939                 
48940                 return true;
48941             },
48942
48943             "esc" : function(e){
48944                 this.collapse();
48945             },
48946
48947             "tab" : function(e){
48948                 this.collapse();
48949                 
48950                 if(this.fireEvent("specialkey", this, e)){
48951                     this.onViewClick(false);
48952                 }
48953                 
48954                 return true;
48955             },
48956
48957             scope : this,
48958
48959             doRelay : function(foo, bar, hname){
48960                 if(hname == 'down' || this.scope.isExpanded()){
48961                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48962                 }
48963                 return true;
48964             },
48965
48966             forceKeyDown: true
48967         });
48968         
48969         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48970         
48971     },
48972     
48973     initNumberEvent : function(e)
48974     {
48975         this.inputEl().on("keydown" , this.fireKey,  this);
48976         this.inputEl().on("focus", this.onFocus,  this);
48977         this.inputEl().on("blur", this.onBlur,  this);
48978         
48979         this.inputEl().relayEvent('keyup', this);
48980         
48981         if(this.indicator){
48982             this.indicator.addClass('invisible');
48983         }
48984  
48985         this.originalValue = this.getValue();
48986         
48987         if(this.validationEvent == 'keyup'){
48988             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48989             this.inputEl().on('keyup', this.filterValidation, this);
48990         }
48991         else if(this.validationEvent !== false){
48992             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48993         }
48994         
48995         if(this.selectOnFocus){
48996             this.on("focus", this.preFocus, this);
48997             
48998         }
48999         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
49000             this.inputEl().on("keypress", this.filterKeys, this);
49001         } else {
49002             this.inputEl().relayEvent('keypress', this);
49003         }
49004         
49005         var allowed = "0123456789";
49006         
49007         if(this.allowDecimals){
49008             allowed += this.decimalSeparator;
49009         }
49010         
49011         if(this.allowNegative){
49012             allowed += "-";
49013         }
49014         
49015         if(this.thousandsDelimiter) {
49016             allowed += ",";
49017         }
49018         
49019         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
49020         
49021         var keyPress = function(e){
49022             
49023             var k = e.getKey();
49024             
49025             var c = e.getCharCode();
49026             
49027             if(
49028                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
49029                     allowed.indexOf(String.fromCharCode(c)) === -1
49030             ){
49031                 e.stopEvent();
49032                 return;
49033             }
49034             
49035             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
49036                 return;
49037             }
49038             
49039             if(allowed.indexOf(String.fromCharCode(c)) === -1){
49040                 e.stopEvent();
49041             }
49042         };
49043         
49044         this.inputEl().on("keypress", keyPress, this);
49045         
49046     },
49047     
49048     onTriggerClick : function(e)
49049     {   
49050         if(this.disabled){
49051             return;
49052         }
49053         
49054         this.page = 0;
49055         this.loadNext = false;
49056         
49057         if(this.isExpanded()){
49058             this.collapse();
49059             return;
49060         }
49061         
49062         this.hasFocus = true;
49063         
49064         if(this.triggerAction == 'all') {
49065             this.doQuery(this.allQuery, true);
49066             return;
49067         }
49068         
49069         this.doQuery(this.getRawValue());
49070     },
49071     
49072     getCurrency : function()
49073     {   
49074         var v = this.currencyEl().getValue();
49075         
49076         return v;
49077     },
49078     
49079     restrictHeight : function()
49080     {
49081         this.list.alignTo(this.currencyEl(), this.listAlign);
49082         this.list.alignTo(this.currencyEl(), this.listAlign);
49083     },
49084     
49085     onViewClick : function(view, doFocus, el, e)
49086     {
49087         var index = this.view.getSelectedIndexes()[0];
49088         
49089         var r = this.store.getAt(index);
49090         
49091         if(r){
49092             this.onSelect(r, index);
49093         }
49094     },
49095     
49096     onSelect : function(record, index){
49097         
49098         if(this.fireEvent('beforeselect', this, record, index) !== false){
49099         
49100             this.setFromCurrencyData(index > -1 ? record.data : false);
49101             
49102             this.collapse();
49103             
49104             this.fireEvent('select', this, record, index);
49105         }
49106     },
49107     
49108     setFromCurrencyData : function(o)
49109     {
49110         var currency = '';
49111         
49112         this.lastCurrency = o;
49113         
49114         if (this.currencyField) {
49115             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49116         } else {
49117             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
49118         }
49119         
49120         this.lastSelectionText = currency;
49121         
49122         //setting default currency
49123         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49124             this.setCurrency(this.defaultCurrency);
49125             return;
49126         }
49127         
49128         this.setCurrency(currency);
49129     },
49130     
49131     setFromData : function(o)
49132     {
49133         var c = {};
49134         
49135         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49136         
49137         this.setFromCurrencyData(c);
49138         
49139         var value = '';
49140         
49141         if (this.name) {
49142             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49143         } else {
49144             Roo.log('no value set for '+ (this.name ? this.name : this.id));
49145         }
49146         
49147         this.setValue(value);
49148         
49149     },
49150     
49151     setCurrency : function(v)
49152     {   
49153         this.currencyValue = v;
49154         
49155         if(this.rendered){
49156             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49157             this.validate();
49158         }
49159     },
49160     
49161     setValue : function(v)
49162     {
49163         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49164         
49165         this.value = v;
49166         
49167         if(this.rendered){
49168             
49169             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49170             
49171             this.inputEl().dom.value = (v == '') ? '' :
49172                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49173             
49174             if(!this.allowZero && v === '0') {
49175                 this.hiddenEl().dom.value = '';
49176                 this.inputEl().dom.value = '';
49177             }
49178             
49179             this.validate();
49180         }
49181     },
49182     
49183     getRawValue : function()
49184     {
49185         var v = this.inputEl().getValue();
49186         
49187         return v;
49188     },
49189     
49190     getValue : function()
49191     {
49192         return this.fixPrecision(this.parseValue(this.getRawValue()));
49193     },
49194     
49195     parseValue : function(value)
49196     {
49197         if(this.thousandsDelimiter) {
49198             value += "";
49199             r = new RegExp(",", "g");
49200             value = value.replace(r, "");
49201         }
49202         
49203         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49204         return isNaN(value) ? '' : value;
49205         
49206     },
49207     
49208     fixPrecision : function(value)
49209     {
49210         if(this.thousandsDelimiter) {
49211             value += "";
49212             r = new RegExp(",", "g");
49213             value = value.replace(r, "");
49214         }
49215         
49216         var nan = isNaN(value);
49217         
49218         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49219             return nan ? '' : value;
49220         }
49221         return parseFloat(value).toFixed(this.decimalPrecision);
49222     },
49223     
49224     decimalPrecisionFcn : function(v)
49225     {
49226         return Math.floor(v);
49227     },
49228     
49229     validateValue : function(value)
49230     {
49231         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49232             return false;
49233         }
49234         
49235         var num = this.parseValue(value);
49236         
49237         if(isNaN(num)){
49238             this.markInvalid(String.format(this.nanText, value));
49239             return false;
49240         }
49241         
49242         if(num < this.minValue){
49243             this.markInvalid(String.format(this.minText, this.minValue));
49244             return false;
49245         }
49246         
49247         if(num > this.maxValue){
49248             this.markInvalid(String.format(this.maxText, this.maxValue));
49249             return false;
49250         }
49251         
49252         return true;
49253     },
49254     
49255     validate : function()
49256     {
49257         if(this.disabled || this.allowBlank){
49258             this.markValid();
49259             return true;
49260         }
49261         
49262         var currency = this.getCurrency();
49263         
49264         if(this.validateValue(this.getRawValue()) && currency.length){
49265             this.markValid();
49266             return true;
49267         }
49268         
49269         this.markInvalid();
49270         return false;
49271     },
49272     
49273     getName: function()
49274     {
49275         return this.name;
49276     },
49277     
49278     beforeBlur : function()
49279     {
49280         if(!this.castInt){
49281             return;
49282         }
49283         
49284         var v = this.parseValue(this.getRawValue());
49285         
49286         if(v || v == 0){
49287             this.setValue(v);
49288         }
49289     },
49290     
49291     onBlur : function()
49292     {
49293         this.beforeBlur();
49294         
49295         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49296             //this.el.removeClass(this.focusClass);
49297         }
49298         
49299         this.hasFocus = false;
49300         
49301         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49302             this.validate();
49303         }
49304         
49305         var v = this.getValue();
49306         
49307         if(String(v) !== String(this.startValue)){
49308             this.fireEvent('change', this, v, this.startValue);
49309         }
49310         
49311         this.fireEvent("blur", this);
49312     },
49313     
49314     inputEl : function()
49315     {
49316         return this.el.select('.roo-money-amount-input', true).first();
49317     },
49318     
49319     currencyEl : function()
49320     {
49321         return this.el.select('.roo-money-currency-input', true).first();
49322     },
49323     
49324     hiddenEl : function()
49325     {
49326         return this.el.select('input.hidden-number-input',true).first();
49327     }
49328     
49329 });/**
49330  * @class Roo.bootstrap.BezierSignature
49331  * @extends Roo.bootstrap.Component
49332  * Bootstrap BezierSignature class
49333  * This script refer to:
49334  *    Title: Signature Pad
49335  *    Author: szimek
49336  *    Availability: https://github.com/szimek/signature_pad
49337  *
49338  * @constructor
49339  * Create a new BezierSignature
49340  * @param {Object} config The config object
49341  */
49342
49343 Roo.bootstrap.BezierSignature = function(config){
49344     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49345     this.addEvents({
49346         "resize" : true
49347     });
49348 };
49349
49350 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49351 {
49352      
49353     curve_data: [],
49354     
49355     is_empty: true,
49356     
49357     mouse_btn_down: true,
49358     
49359     /**
49360      * @cfg {int} canvas height
49361      */
49362     canvas_height: '200px',
49363     
49364     /**
49365      * @cfg {float|function} Radius of a single dot.
49366      */ 
49367     dot_size: false,
49368     
49369     /**
49370      * @cfg {float} Minimum width of a line. Defaults to 0.5.
49371      */
49372     min_width: 0.5,
49373     
49374     /**
49375      * @cfg {float} Maximum width of a line. Defaults to 2.5.
49376      */
49377     max_width: 2.5,
49378     
49379     /**
49380      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49381      */
49382     throttle: 16,
49383     
49384     /**
49385      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49386      */
49387     min_distance: 5,
49388     
49389     /**
49390      * @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.
49391      */
49392     bg_color: 'rgba(0, 0, 0, 0)',
49393     
49394     /**
49395      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49396      */
49397     dot_color: 'black',
49398     
49399     /**
49400      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49401      */ 
49402     velocity_filter_weight: 0.7,
49403     
49404     /**
49405      * @cfg {function} Callback when stroke begin. 
49406      */
49407     onBegin: false,
49408     
49409     /**
49410      * @cfg {function} Callback when stroke end.
49411      */
49412     onEnd: false,
49413     
49414     getAutoCreate : function()
49415     {
49416         var cls = 'roo-signature column';
49417         
49418         if(this.cls){
49419             cls += ' ' + this.cls;
49420         }
49421         
49422         var col_sizes = [
49423             'lg',
49424             'md',
49425             'sm',
49426             'xs'
49427         ];
49428         
49429         for(var i = 0; i < col_sizes.length; i++) {
49430             if(this[col_sizes[i]]) {
49431                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49432             }
49433         }
49434         
49435         var cfg = {
49436             tag: 'div',
49437             cls: cls,
49438             cn: [
49439                 {
49440                     tag: 'div',
49441                     cls: 'roo-signature-body',
49442                     cn: [
49443                         {
49444                             tag: 'canvas',
49445                             cls: 'roo-signature-body-canvas',
49446                             height: this.canvas_height,
49447                             width: this.canvas_width
49448                         }
49449                     ]
49450                 },
49451                 {
49452                     tag: 'input',
49453                     type: 'file',
49454                     style: 'display: none'
49455                 }
49456             ]
49457         };
49458         
49459         return cfg;
49460     },
49461     
49462     initEvents: function() 
49463     {
49464         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49465         
49466         var canvas = this.canvasEl();
49467         
49468         // mouse && touch event swapping...
49469         canvas.dom.style.touchAction = 'none';
49470         canvas.dom.style.msTouchAction = 'none';
49471         
49472         this.mouse_btn_down = false;
49473         canvas.on('mousedown', this._handleMouseDown, this);
49474         canvas.on('mousemove', this._handleMouseMove, this);
49475         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49476         
49477         if (window.PointerEvent) {
49478             canvas.on('pointerdown', this._handleMouseDown, this);
49479             canvas.on('pointermove', this._handleMouseMove, this);
49480             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49481         }
49482         
49483         if ('ontouchstart' in window) {
49484             canvas.on('touchstart', this._handleTouchStart, this);
49485             canvas.on('touchmove', this._handleTouchMove, this);
49486             canvas.on('touchend', this._handleTouchEnd, this);
49487         }
49488         
49489         Roo.EventManager.onWindowResize(this.resize, this, true);
49490         
49491         // file input event
49492         this.fileEl().on('change', this.uploadImage, this);
49493         
49494         this.clear();
49495         
49496         this.resize();
49497     },
49498     
49499     resize: function(){
49500         
49501         var canvas = this.canvasEl().dom;
49502         var ctx = this.canvasElCtx();
49503         var img_data = false;
49504         
49505         if(canvas.width > 0) {
49506             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49507         }
49508         // setting canvas width will clean img data
49509         canvas.width = 0;
49510         
49511         var style = window.getComputedStyle ? 
49512             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49513             
49514         var padding_left = parseInt(style.paddingLeft) || 0;
49515         var padding_right = parseInt(style.paddingRight) || 0;
49516         
49517         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49518         
49519         if(img_data) {
49520             ctx.putImageData(img_data, 0, 0);
49521         }
49522     },
49523     
49524     _handleMouseDown: function(e)
49525     {
49526         if (e.browserEvent.which === 1) {
49527             this.mouse_btn_down = true;
49528             this.strokeBegin(e);
49529         }
49530     },
49531     
49532     _handleMouseMove: function (e)
49533     {
49534         if (this.mouse_btn_down) {
49535             this.strokeMoveUpdate(e);
49536         }
49537     },
49538     
49539     _handleMouseUp: function (e)
49540     {
49541         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49542             this.mouse_btn_down = false;
49543             this.strokeEnd(e);
49544         }
49545     },
49546     
49547     _handleTouchStart: function (e) {
49548         
49549         e.preventDefault();
49550         if (e.browserEvent.targetTouches.length === 1) {
49551             // var touch = e.browserEvent.changedTouches[0];
49552             // this.strokeBegin(touch);
49553             
49554              this.strokeBegin(e); // assume e catching the correct xy...
49555         }
49556     },
49557     
49558     _handleTouchMove: function (e) {
49559         e.preventDefault();
49560         // var touch = event.targetTouches[0];
49561         // _this._strokeMoveUpdate(touch);
49562         this.strokeMoveUpdate(e);
49563     },
49564     
49565     _handleTouchEnd: function (e) {
49566         var wasCanvasTouched = e.target === this.canvasEl().dom;
49567         if (wasCanvasTouched) {
49568             e.preventDefault();
49569             // var touch = event.changedTouches[0];
49570             // _this._strokeEnd(touch);
49571             this.strokeEnd(e);
49572         }
49573     },
49574     
49575     reset: function () {
49576         this._lastPoints = [];
49577         this._lastVelocity = 0;
49578         this._lastWidth = (this.min_width + this.max_width) / 2;
49579         this.canvasElCtx().fillStyle = this.dot_color;
49580     },
49581     
49582     strokeMoveUpdate: function(e)
49583     {
49584         this.strokeUpdate(e);
49585         
49586         if (this.throttle) {
49587             this.throttleStroke(this.strokeUpdate, this.throttle);
49588         }
49589         else {
49590             this.strokeUpdate(e);
49591         }
49592     },
49593     
49594     strokeBegin: function(e)
49595     {
49596         var newPointGroup = {
49597             color: this.dot_color,
49598             points: []
49599         };
49600         
49601         if (typeof this.onBegin === 'function') {
49602             this.onBegin(e);
49603         }
49604         
49605         this.curve_data.push(newPointGroup);
49606         this.reset();
49607         this.strokeUpdate(e);
49608     },
49609     
49610     strokeUpdate: function(e)
49611     {
49612         var rect = this.canvasEl().dom.getBoundingClientRect();
49613         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49614         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49615         var lastPoints = lastPointGroup.points;
49616         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49617         var isLastPointTooClose = lastPoint
49618             ? point.distanceTo(lastPoint) <= this.min_distance
49619             : false;
49620         var color = lastPointGroup.color;
49621         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49622             var curve = this.addPoint(point);
49623             if (!lastPoint) {
49624                 this.drawDot({color: color, point: point});
49625             }
49626             else if (curve) {
49627                 this.drawCurve({color: color, curve: curve});
49628             }
49629             lastPoints.push({
49630                 time: point.time,
49631                 x: point.x,
49632                 y: point.y
49633             });
49634         }
49635     },
49636     
49637     strokeEnd: function(e)
49638     {
49639         this.strokeUpdate(e);
49640         if (typeof this.onEnd === 'function') {
49641             this.onEnd(e);
49642         }
49643     },
49644     
49645     addPoint:  function (point) {
49646         var _lastPoints = this._lastPoints;
49647         _lastPoints.push(point);
49648         if (_lastPoints.length > 2) {
49649             if (_lastPoints.length === 3) {
49650                 _lastPoints.unshift(_lastPoints[0]);
49651             }
49652             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49653             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49654             _lastPoints.shift();
49655             return curve;
49656         }
49657         return null;
49658     },
49659     
49660     calculateCurveWidths: function (startPoint, endPoint) {
49661         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49662             (1 - this.velocity_filter_weight) * this._lastVelocity;
49663
49664         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49665         var widths = {
49666             end: newWidth,
49667             start: this._lastWidth
49668         };
49669         
49670         this._lastVelocity = velocity;
49671         this._lastWidth = newWidth;
49672         return widths;
49673     },
49674     
49675     drawDot: function (_a) {
49676         var color = _a.color, point = _a.point;
49677         var ctx = this.canvasElCtx();
49678         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49679         ctx.beginPath();
49680         this.drawCurveSegment(point.x, point.y, width);
49681         ctx.closePath();
49682         ctx.fillStyle = color;
49683         ctx.fill();
49684     },
49685     
49686     drawCurve: function (_a) {
49687         var color = _a.color, curve = _a.curve;
49688         var ctx = this.canvasElCtx();
49689         var widthDelta = curve.endWidth - curve.startWidth;
49690         var drawSteps = Math.floor(curve.length()) * 2;
49691         ctx.beginPath();
49692         ctx.fillStyle = color;
49693         for (var i = 0; i < drawSteps; i += 1) {
49694         var t = i / drawSteps;
49695         var tt = t * t;
49696         var ttt = tt * t;
49697         var u = 1 - t;
49698         var uu = u * u;
49699         var uuu = uu * u;
49700         var x = uuu * curve.startPoint.x;
49701         x += 3 * uu * t * curve.control1.x;
49702         x += 3 * u * tt * curve.control2.x;
49703         x += ttt * curve.endPoint.x;
49704         var y = uuu * curve.startPoint.y;
49705         y += 3 * uu * t * curve.control1.y;
49706         y += 3 * u * tt * curve.control2.y;
49707         y += ttt * curve.endPoint.y;
49708         var width = curve.startWidth + ttt * widthDelta;
49709         this.drawCurveSegment(x, y, width);
49710         }
49711         ctx.closePath();
49712         ctx.fill();
49713     },
49714     
49715     drawCurveSegment: function (x, y, width) {
49716         var ctx = this.canvasElCtx();
49717         ctx.moveTo(x, y);
49718         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49719         this.is_empty = false;
49720     },
49721     
49722     clear: function()
49723     {
49724         var ctx = this.canvasElCtx();
49725         var canvas = this.canvasEl().dom;
49726         ctx.fillStyle = this.bg_color;
49727         ctx.clearRect(0, 0, canvas.width, canvas.height);
49728         ctx.fillRect(0, 0, canvas.width, canvas.height);
49729         this.curve_data = [];
49730         this.reset();
49731         this.is_empty = true;
49732     },
49733     
49734     fileEl: function()
49735     {
49736         return  this.el.select('input',true).first();
49737     },
49738     
49739     canvasEl: function()
49740     {
49741         return this.el.select('canvas',true).first();
49742     },
49743     
49744     canvasElCtx: function()
49745     {
49746         return this.el.select('canvas',true).first().dom.getContext('2d');
49747     },
49748     
49749     getImage: function(type)
49750     {
49751         if(this.is_empty) {
49752             return false;
49753         }
49754         
49755         // encryption ?
49756         return this.canvasEl().dom.toDataURL('image/'+type, 1);
49757     },
49758     
49759     drawFromImage: function(img_src)
49760     {
49761         var img = new Image();
49762         
49763         img.onload = function(){
49764             this.canvasElCtx().drawImage(img, 0, 0);
49765         }.bind(this);
49766         
49767         img.src = img_src;
49768         
49769         this.is_empty = false;
49770     },
49771     
49772     selectImage: function()
49773     {
49774         this.fileEl().dom.click();
49775     },
49776     
49777     uploadImage: function(e)
49778     {
49779         var reader = new FileReader();
49780         
49781         reader.onload = function(e){
49782             var img = new Image();
49783             img.onload = function(){
49784                 this.reset();
49785                 this.canvasElCtx().drawImage(img, 0, 0);
49786             }.bind(this);
49787             img.src = e.target.result;
49788         }.bind(this);
49789         
49790         reader.readAsDataURL(e.target.files[0]);
49791     },
49792     
49793     // Bezier Point Constructor
49794     Point: (function () {
49795         function Point(x, y, time) {
49796             this.x = x;
49797             this.y = y;
49798             this.time = time || Date.now();
49799         }
49800         Point.prototype.distanceTo = function (start) {
49801             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49802         };
49803         Point.prototype.equals = function (other) {
49804             return this.x === other.x && this.y === other.y && this.time === other.time;
49805         };
49806         Point.prototype.velocityFrom = function (start) {
49807             return this.time !== start.time
49808             ? this.distanceTo(start) / (this.time - start.time)
49809             : 0;
49810         };
49811         return Point;
49812     }()),
49813     
49814     
49815     // Bezier Constructor
49816     Bezier: (function () {
49817         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49818             this.startPoint = startPoint;
49819             this.control2 = control2;
49820             this.control1 = control1;
49821             this.endPoint = endPoint;
49822             this.startWidth = startWidth;
49823             this.endWidth = endWidth;
49824         }
49825         Bezier.fromPoints = function (points, widths, scope) {
49826             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49827             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49828             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49829         };
49830         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49831             var dx1 = s1.x - s2.x;
49832             var dy1 = s1.y - s2.y;
49833             var dx2 = s2.x - s3.x;
49834             var dy2 = s2.y - s3.y;
49835             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49836             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49837             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49838             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49839             var dxm = m1.x - m2.x;
49840             var dym = m1.y - m2.y;
49841             var k = l2 / (l1 + l2);
49842             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49843             var tx = s2.x - cm.x;
49844             var ty = s2.y - cm.y;
49845             return {
49846                 c1: new scope.Point(m1.x + tx, m1.y + ty),
49847                 c2: new scope.Point(m2.x + tx, m2.y + ty)
49848             };
49849         };
49850         Bezier.prototype.length = function () {
49851             var steps = 10;
49852             var length = 0;
49853             var px;
49854             var py;
49855             for (var i = 0; i <= steps; i += 1) {
49856                 var t = i / steps;
49857                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49858                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49859                 if (i > 0) {
49860                     var xdiff = cx - px;
49861                     var ydiff = cy - py;
49862                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49863                 }
49864                 px = cx;
49865                 py = cy;
49866             }
49867             return length;
49868         };
49869         Bezier.prototype.point = function (t, start, c1, c2, end) {
49870             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49871             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49872             + (3.0 * c2 * (1.0 - t) * t * t)
49873             + (end * t * t * t);
49874         };
49875         return Bezier;
49876     }()),
49877     
49878     throttleStroke: function(fn, wait) {
49879       if (wait === void 0) { wait = 250; }
49880       var previous = 0;
49881       var timeout = null;
49882       var result;
49883       var storedContext;
49884       var storedArgs;
49885       var later = function () {
49886           previous = Date.now();
49887           timeout = null;
49888           result = fn.apply(storedContext, storedArgs);
49889           if (!timeout) {
49890               storedContext = null;
49891               storedArgs = [];
49892           }
49893       };
49894       return function wrapper() {
49895           var args = [];
49896           for (var _i = 0; _i < arguments.length; _i++) {
49897               args[_i] = arguments[_i];
49898           }
49899           var now = Date.now();
49900           var remaining = wait - (now - previous);
49901           storedContext = this;
49902           storedArgs = args;
49903           if (remaining <= 0 || remaining > wait) {
49904               if (timeout) {
49905                   clearTimeout(timeout);
49906                   timeout = null;
49907               }
49908               previous = now;
49909               result = fn.apply(storedContext, storedArgs);
49910               if (!timeout) {
49911                   storedContext = null;
49912                   storedArgs = [];
49913               }
49914           }
49915           else if (!timeout) {
49916               timeout = window.setTimeout(later, remaining);
49917           }
49918           return result;
49919       };
49920   }
49921   
49922 });
49923
49924  
49925
49926  // old names for form elements
49927 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
49928 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
49929 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
49930 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
49931 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
49932 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
49933 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
49934 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
49935 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
49936 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
49937 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
49938 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
49939 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
49940 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
49941 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
49942 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
49943 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
49944 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
49945 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
49946 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
49947 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
49948 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
49949 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
49950 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
49951 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
49952 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
49953
49954 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
49955 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49956
49957 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
49958 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
49959
49960 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
49961 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49962 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
49963 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
49964